15. Tìm hiểu về cách sử dụng các tính năng nâng cao của Flutter
SingleChildScrollView và ListView là hai widget trong Flutter dùng để tạo ra các cuộn.
SingleChildScrollView là một widget cuộn dùng để hiển thị một widget con duy nhất, nó cho phép cuộn nội dung theo chiều ngang hoặc chiều dọc. Ví dụ như, bạn có thể sử dụng SingleChildScrollView để tạo một cuộn chứa một TextField hoặc một Column chứa nhiều widget con khác nhau.
SingleChildScrollView( child: Column( children: <Widget>[ TextField( decoration: InputDecoration( hintText: 'Enter some text', ), ), Container( padding: EdgeInsets.all(10), child: Text('This is the content of the ScrollView'), ), Container( padding: EdgeInsets.all(10), child: Text('You can scroll through this content'), ), Container( padding: EdgeInsets.all(10), child: Text('Vertically or Horizontally'), ), ], ),
)
Các property có thể sử dụng để điều chỉnh hành vi cuộn của SingleChildScrollView bao gồm:
physics: điều chỉnh hành vi cuộn, bạn có thể sử dụng các giá trị như AlwaysScrollableScrollPhysics để cho phép cuộn mặc dù nội dung không đủ dài, hoặc BouncingScrollPhysics để cho phép cuộn có hiệu ứng phản lực.
controller: điều khiển cuộn bằng code, cho phép bạn làm việc với vị trí cuộn hiện tại, tự động cuộn đến vị trí nào đó hoặc lắng nghe sự thay đổi vị trí cuộn
reverse: xác định chiều cuộn, có thể đặt là true để cuộn từ dưới lên hoặc false để cuộn từ trên xuống.
primary: xác định cuộn chính, bạn có thể sử dụng để chỉ định cuộn chính trong trường hợp bạn có nhiều cuộn trong một màn hình.
padding: chỉ định khoảng cách giữa bề mặt cuộn và nội dung.
Ví dụ, để cho phép cuộn theo chiều ngang và tự động cuộn đến vị trí cuối cùng của nội dung khi nó được tải:
SingleChildScrollView( scrollDirection: Axis.horizontal, physics: AlwaysScrollableScrollPhysics(), child: Column( children: <Widget>[ TextField(...), ... ], ), controller: ScrollController(keepScrollOffset: false),
)
Như vậy, cách sử dụng SingleChildScrollView và các property của nó để tạo ra các cuộn trong ứng dụng Flutter rất đa dạng và linh hoạt, tùy thuộc vào yêu cầu và mục đích của bạn. bạn có thể thử sử dụng SingleChildScrollView với các widget con khác nhau, ví dụ như Image, Card hay ListTile, để tạo ra các cuộn với giao diện đẹp hơn và có nhiều tùy chọn khác nhau.
Bạn cũng có thể sử dụng SingleChildScrollView kết hợp với các widget như SliverAppBar và SliverList để tạo ra các cuộn có hiệu ứng mượt mà hơn và tùy chỉnh hơn.
Tôi khuyến cáo bạn tìm hiểu và thử sử dụng các widget cuộn khác trong Flutter như ListView, GridView hoặc CustomScrollView để tạo ra các cuộn với các tùy chọn và hành vi khác nhau.
ListView là một widget cuộn dùng để hiển thị danh sách các widget con. Nó cho phép cuộn nội dung theo chiều dọc. Bạn có thể sử dụng ListView để tạo một danh sách các mục, ví dụ như một danh sách các sản phẩm hoặc tin tức.
ListView( children: <Widget>[ ListTile(title: Text('Item 1')), ListTile(title: Text('Item 2')), ... ],
)
Cả hai SingleChildScrollView và ListView có thể sử dụng physics để điều chỉnh hành vi cuộn, ví dụ như cho phép cuộn hoặc không cho phép cuộn theo chiều ngang. Cũng có thể sử dụng controller để điều khiển cuộn bằng code.
Sử dụng package flutter_localizations để hỗ trợ ngôn ngữ và địa phương trong ứng dụng của bạn.
Sử dụng InheritedWidget để quản lý trạng thái và truyền dữ liệu giữa các widget con.
Ví dụ về việc sử dụng InheritedWidget để quản lý trạng thái cho ứng dụng:
class MyInheritedWidget extends InheritedWidget { final String data; MyInheritedWidget({ Key key, @required Widget child, this.data, }) : super(key: key, child: child); @override bool updateShouldNotify(MyInheritedWidget oldWidget) { return data != oldWidget.data; } static MyInheritedWidget of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>(); }
}
Trong ví dụ trên, ta tạo một lớp MyInheritedWidget kế thừa từ InheritedWidget và chứa một thuộc tính dữ liệu data.
Hàm updateShouldNotify được gọi mỗi khi dữ liệu thay đổi và nếu trả về true thì builder của tất cả InheritedWidget sẽ gọi lại.
Hàm of cho phép bạn truy cập đến thuộc tính data trong bất kỳ vị trí của tree widget.
class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return MyInheritedWidget( data: 'Hello World', child: Scaffold( body: Center( child: Text(MyInheritedWidget.of(context).data), ), ), ); }
}
Trong ví dụ này, ta sử dụng MyInheritedWidget để quản lý dữ liệu Hello World và sử dụng MyInheritedWidget.of(context).data để truy cập dữ liệu trong trang MyHomePage
InheritedWidget là một cách tiếp cận tốt để quản lý trạng thái toàn cục trong ứng dụng Flutter của bạn. Nó cho phép bạn chia sẻ dữ liệu giữa các widget mà không cần sử dụng các biến toàn cục hoặc các GlobalKey. InheritedWidget cũng giúp cho việc quản lý trạng thái trở nên dễ dàng hơn và giảm số lượng setState() cần thiết.
Tuy nhiên, lưu ý rằng sử dụng InheritedWidget có thể gây ra một số vấn đề về performance nếu sử dụng không chính xác. Vì vậy, hãy chắc chắn rằng bạn chỉ sử dụng InheritedWidget nếu cần thiết và sử dụng nó một cách hiệu quả.
Sử dụng Animation để tạo ra các hiệu ứng chuyển động và AnimatedBuilder để tạo ra các widget có hiệu ứng chuyển động tùy chỉnh.
Ví dụ về việc sử dụng Animation:
class FadeAnimation extends StatelessWidget { final double delay; final Widget child; FadeAnimation({this.delay, this.child}); @override Widget build(BuildContext context) { final tween = MultiTrackTween([ Track("opacity") .add(Duration(milliseconds: 500), Tween(begin: 0.0, end: 1.0)), Track("translateX").add( Duration(milliseconds: 500), Tween(begin: -30.0, end: 0.0), curve: Curves.easeOut) ]); return ControlledAnimation( delay: Duration(milliseconds: (500 * delay).round()), duration: tween.duration, tween: tween, child: child, builderWithChild: (context, child, animation) => Opacity( opacity: animation["opacity"], child: Transform.translate( offset: Offset(animation["translateX"], 0), child: child), ), ); }
}
Bạn có thể sử dụng nó như sau:
FadeAnimation( delay: 1, child: Text("Hello World"),
)
Ngoài ra, có nhiều Animation khác như: ScaleAnimation, RotationAnimation, SizeAnimation...
Các animation cùng với AnimatedBuilder giúp cho việc tùy chỉnh giao diện và tạo ra các hiệu ứng chuyển động được dễ dàng hơn. Các package của thư viện của Flutter cung cấp nhiều animation khác nhau với cách sử dụng khác nhau giúp cho bạn có nhiều tùy chọn.
Sử dụng StreamBuilder để theo dõi và hiển thị dữ liệu từ các luồng dữ liệu.
Ví dụ về việc sử dụng StreamBuilder để hiển thị dữ liệu từ một luồng dữ liệu:
class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState();
} class _MyHomePageState extends State<MyHomePage> { final Stream<int> _counterStream = Stream.periodic(Duration(seconds: 1), (i) => i).take(10); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: StreamBuilder<int>( stream: _counterStream, initialData: 0, builder: (context, snapshot) { return Text('You hit me: ${snapshot.data} times'); }, ), ), ); }
}
Trong ví dụ trên, StreamBuilder sẽ theo dõi luồn dữ liệu _counterStream và sử dụng dữ liệu mới nhất để cập nhật giao diện. Các giá trị trong stream là int và sẽ tăng mỗi giây một lần.
Lưu ý rằng StreamBuilder cần một giá trị initialData để hiển thị trước khi nhận được dữ liệu từ luồng. builder là một hàm callback, nó sẽ được gọi mỗi khi có dữ liệu mới từ luồng và cập nhật giao diện tương ứng.
Sử dụng StreamBuilder cho phép bạn theo dõi dữ liệu từ luồng và cập nhật giao diện một cách dễ dàng mà không cần phải quản lý trạng thái hoặc làm việc với setState.