- SplashScreen là gì?
- Thư viện SplashScreen
- Example: Time based
- Example: Time based + route Map
- Example: Time based + pageRoute
- Example: Time based + routeName
- Example: Future based
- Example: Future based + route Map
- loaderColor
- navigateAfterFuture
- seconds
- pageRoute
- onClick
- navigateAfterSeconds
- title
- backgroundColor
- styleTextUnderTheLoader
- image
- photoSize
- loadingText
- useLoader
- imageBackground
- gradientBackground
- routeName
Hướng dẫn và ví dụ Flutter SplashScreen
1. SplashScreen là gì?
Splash Screen là màn hình người dùng nhìn thấy đầu tiên khi môt ứng dụng được khởi chạy. Nó cũng có thể là màn hình chào mừng của ứng dụng cung cấp trải nghiệm ban đầu đơn giản khi một trò chơi hoặc chương trình di động đang khởi chạy. Đôi khi Splash Screen được sử dụng để cho người dùng xem trạng thái ứng dụng đang được tải lên bộ nhớ trước khi nó sẵn sàng hoạt động.
Sau đây là các đặc điểm cơ bản của Splash Screen:
- Nó chủ yếu được sử dụng để xây dựng thương hiệu hoặc nhận dạng danh tính của ứng dụng và tạo ấn tượng thương hiệu cho người dùng.
- Nó cũng được sử dụng để hiển thị tiến trình ứng dụng được tải lên bộ nhớ để trình bầy phần mềm cho người dùng.
- Khi tải xong Splash Screen, người dùng sẽ nhận được một màn hình chức năng khác sẽ là màn hình chính hoặc trang tổng quan (dashboard), sau đó nó sẽ bị lãng quên. Khi quá trình tải hoàn tất, chúng ta không thể nhấn nút quay lại để quay lại Splash Screen.
2. Thư viện SplashScreen
Trong bài viết này tôi sẽ hướng dẫn bạn sử dụng thư viện splashscreen được cung cấp bởi cộng đồng, nó không sẵn có trong thư viện tiêu chuẩn của Flutter vì vậy để sử dụng bạn phải khai báo nó trong project.
Trên project, mở file pubspec.yaml và khai báo thư viện splashscreen:
pubspec.yaml
.....
dependencies:
flutter:
sdk: flutter
splashscreen: ^1.3.5
Bạn có thể tìm thấy các phiên bản khác hoặc mới nhất của thư viện splashscreen tại liên kết dưới đây:
Lớp SplashScreen cung cấp 3 constructor:
SplashScreen Constructor
@protected
SplashScreen(
{Color loaderColor,
Future navigateAfterFuture,
int seconds,
double photoSize,
Route pageRoute,
dynamic onClick,
dynamic navigateAfterSeconds,
Text title: const Text(''),
Color backgroundColor: Colors.white,
TextStyle styleTextUnderTheLoader:
const TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black),
Image image,
Text loadingText: const Text(""),
ImageProvider<Object> imageBackground,
Gradient gradientBackground,
bool useLoader: true,
String routeName}
)
SplashScreen.network Constructor
SplashScreen.network(
{@required Future navigateAfterFuture,
Color loaderColor,
Color backgroundColor,
double photoSize,
Text loadingText,
Image image,
Route pageRoute,
dynamic onClick,
dynamic navigateAfterSeconds,
Text title,
TextStyle styleTextUnderTheLoader,
ImageProvider<Object> imageBackground,
Gradient gradientBackground,
bool useLoader,
String routeName}
)
SplashScreen.timer Constructor
SplashScreen.timer(
{@required int seconds,
Color loaderColor,
Color backgroundColor,
double photoSize,
Text loadingText,
Image image,
Route pageRoute,
dynamic onClick,
dynamic navigateAfterSeconds,
Text title,
TextStyle styleTextUnderTheLoader,
ImageProvider<Object> imageBackground,
Gradient gradientBackground,
bool useLoader,
String routeName}
)
Bố cục của SplashScreen:
3. Example: Time based
Trước hết chúng ta sẽ bắt đầu với một ví dụ đơn giản, một Splash Screen hiển thị trong một khoảng thời gian được chỉ định, sau đó nó sẽ được thay thế bởi màn hình chính của ứng dụng. Hãy xem trước kết quả của ví dụ:
Trước hết, bạn cần tạo một dự án Flutter và khai báo thư viện splashscreen vào dự án của bạn như đã được đề cập ở phía trên.
Tiếp theo, mở file main.dart và thay thế nó nội dung của nó bởi code dưới đây:
main.dart (ex1)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:splashscreen/splashscreen.dart';
void main() { runApp(MyApp()); }
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreenPage(),
debugShowCheckedModeBanner: false,
);
}
}
class SplashScreenPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SplashScreen(
seconds: 5,
navigateAfterSeconds: new HomeScreen(),
backgroundColor: Colors.green,
title: new Text('o7planning.org',textScaleFactor: 2,),
image: new Image.network(
'https://s3.o7planning.com/images/triceratops/image1.png'
),
loadingText: Text("Loading"),
photoSize: 110.0,
loaderColor: Colors.red,
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:Text("Flutter SplashScreen")
),
body: Center(
child:Text("Welcome to Home Page",
style: TextStyle( color: Colors.black, fontSize: 30)
)
),
);
}
}
Code ở trên cho thấy rằng ban đầu màn hình chính của ứng dụng được tạo ra bởi SplashScreenPage, phương thức build() của lớp này trả về một đối tượng SplashScreen.
Tham số seconds chỉ định số giây Splash Screen sẽ hiển thị, sau khi hết thời gian nó sẽ được thay thế bởi HomeScreen (Được chỉ định bởi tham số navigateAfterSeconds).
4. Example: Time based + route Map
Nếu ứng dụng của bạn sử dụng route Map để điều hướng các trang bạn có thể đặt SplashScreen ở phía trước giống như hình minh hoạ dưới đây:
Trong tình huống này tham số navigateAfterSeconds là kiểu String, nó là tên của một trang mà bạn muốn chuyển hướng đến.
main.dart (ex2)
import 'package:flutter/material.dart';
import 'package:splashscreen/splashscreen.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'o7planning.org',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SplashScreenPage(),
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => HomePage(),
'/details': (BuildContext context) => DetailsPage(),
'/about': (BuildContext context) => AboutPage(),
},
);
}
}
class SplashScreenPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SplashScreen(
title: new Text('Welcome In SplashScreen' ),
image: new Image.network(
'https://s3.o7planning.com/images/triceratops/image1.png'
),
backgroundColor: Colors.white,
photoSize: 100.0,
loaderColor: Colors.red,
seconds: 15,
navigateAfterSeconds: "/home"
);
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of Home Page"),
),
body: Center(
child: Row (
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
child: Text('Go to Details Page'),
onPressed: () {
Navigator.of(context).pushNamed('/details');
},
),
ElevatedButton(
child: Text('Go to About Page'),
onPressed: () {
Navigator.of(context).pushNamed('/about');
},
),
],
)
),
);
}
}
class DetailsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of Details Page"),
),
body: Center(
child: ElevatedButton(
child: Text('Close'),
onPressed: () {
// Close page and pass a value back to previous page
Navigator.of(context).pop();
},
),
),
backgroundColor: Colors.lightGreen[100],
);
}
}
class AboutPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of About Page"),
),
body: Center(
child: ElevatedButton(
child: Text('Close'),
onPressed: () {
// Close page
Navigator.of(context).pop();
},
),
),
backgroundColor: Colors.cyan[100],
);
}
}
5. Example: Time based + pageRoute
Trong Flutter sử dụng Navigator & Route bạn có thể nhẩy từ một trang này sang một trang khác, và đây chính là một ý tưởng để bạn chuyển từ Splash Screen tới màn hình chính của ứng dụng. Điều quan trọng là lớp SplashScreen cũng hỗ trợ Route, chúng ta hãy xem một ví dụ:
main.dart (ex3)
import 'package:flutter/material.dart';
import 'package:splashscreen/splashscreen.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'o7planning.org',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SplashScreenPage()
);
}
}
class SplashScreenPage extends StatefulWidget {
@override
_SplashScreenPageState createState() => _SplashScreenPageState();
}
class _SplashScreenPageState extends State<SplashScreenPage> {
@override
Widget build(BuildContext context) {
return SplashScreen(
title: new Text('Welcome In SplashScreen'),
image: new Image.network(
'https://s3.o7planning.com/images/triceratops/image1.png'
),
backgroundColor: Colors.white,
photoSize: 100.0,
loaderColor: Colors.red,
seconds: 10,
navigateAfterSeconds: Text("Any Widget"), // Any not null widget.
pageRoute: _createRoute()
);
}
Route _createRoute() {
return PageRouteBuilder(
pageBuilder: (BuildContext context, Animation<double> animation,//
Animation<double> secondaryAnimation) {
return HomeScreen();
},
transitionsBuilder: (BuildContext context, Animation<double> animation, //
Animation<double> secondaryAnimation, Widget child) {
return child;
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:Text("Flutter SplashScreen")
),
body: Center(
child:Text("Welcome to Home Page",
style: TextStyle( color: Colors.black, fontSize: 30)
)
),
backgroundColor: Colors.green[100],
);
}
}
Chú ý: Trong ví dụ này bạn cần sét đặt giá trị cho các tham số seconds, navigateAfterSeconds và pageRoute.
- Tham số pageRoute sẽ được sử dụng nếu nó không null.
- Tham số navigateAfterSeconds là một Widget bất kỳ và không null, mặc dù nó không được sử dụng nhưng bạn cần phải sét đặt giá trị cho nó.
SplashScreen(
...
seconds: 10,
navigateAfterSeconds: Text("Any Widget"), // Any not null widget.
pageRoute: _createRoute()
);
7. Example: Future based
Trong một số tình huống Splash Screen được sử dụng để biểu thị trạng thái ứng dụng đang được tải vào bộ nhớ trước khi ứng dụng sẵn sàng sử dụng đối với người dùng. Khoảng thời gian mà Splash Screen hiển thị phụ thuộc vào sức mạnh của thiết bị. Sau khi Splash Screen hoàn thành các nhiệm vụ, nó sẽ được thay thế bởi màn hình chính của ứng dụng.
Về mặt kỹ thuật màn hình chính sẽ được gói trong một đối tượng Future (Tương lai) để sẵn sàng thay thế Splash Screen khi cần thiết.
Hãy xem trước ví dụ này:
Trong ví dụ này bạn cần sét đặt giá trị cho tham số navigateAfterFuture và không sét đặt giá trị cho tham số seconds.
main.dart (ex7)
import 'package:flutter/material.dart';
import 'package:splashscreen/splashscreen.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'o7planning.org',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SplashScreenPage()
);
}
}
class SplashScreenPage extends StatefulWidget {
@override
_SplashScreenPageState createState() => _SplashScreenPageState();
}
class _SplashScreenPageState extends State<SplashScreenPage> {
int loadingPercent = 0;
Future<Widget> loadFromFuture() async {
// <fetch data from server. ex. login>
while(this.loadingPercent < 100) {
this.setState(() {
this.loadingPercent++;
print("Percent: " + this.loadingPercent.toString());
});
// Delay 100 millisecond.
await Future.delayed(const Duration(milliseconds : 100));
}
// Show Main Screen (After Splash Screen)
return Future.value(HomeScreen());
}
@override
Widget build(BuildContext context) {
return SplashScreen(
navigateAfterFuture: loadFromFuture(),
title: Text(
'Welcome In SplashScreen',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0
),),
image: Image.network(
'https://s3.o7planning.com/images/triceratops/image1.png'
),
backgroundColor: Colors.white,
styleTextUnderTheLoader: TextStyle(),
photoSize: 100.0,
onClick: () => print("Flutter o7planning.org"),
loadingText: Text("Loading " + this.loadingPercent.toString() +" %"),
loaderColor: Colors.red
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter SplashScreen Package"),
automaticallyImplyLeading: false
),
body: Center(
child: Text(
"Welcome To Home Page!",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30.0
),
),
),
);
}
}
8. Example: Future based + route Map
Tiếp theo chúng ta sẽ đến với một ví dụ phức tạp hơn, ứng dụng của bạn sử dụng route Map để điều hướng các trang, và SplashScreen được sử dụng để biểu thị trạng thái ứng dụng đang được tải vào bộ nhớ trước khi ứng dụng sẵn sàng sử dụng đối với người dùng.
Xem trước ví dụ:
main.dart (ex8)
import 'package:flutter/material.dart';
import 'package:splashscreen/splashscreen.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'o7planning.org',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SplashScreenPage(),
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => HomePage(),
'/details': (BuildContext context) => DetailsPage(),
'/about': (BuildContext context) => AboutPage(),
},
);
}
}
class SplashScreenPage extends StatefulWidget {
@override
_SplashScreenPageState createState() => _SplashScreenPageState();
}
class _SplashScreenPageState extends State<SplashScreenPage> {
int loadingPercent = 0;
Future<String> loadFromFuture() async {
// <fetch data from server. ex. login>
while(this.loadingPercent < 100) {
this.setState(() {
this.loadingPercent++;
print("Percent: " + this.loadingPercent.toString());
});
// Delay 100 millisecond.
await Future.delayed(const Duration(milliseconds : 100));
}
// Show Main Screen (After Splash Screen)
return Future.value('/home');
}
@override
Widget build(BuildContext context) {
return SplashScreen(
navigateAfterFuture: loadFromFuture(),
title: Text(
'Welcome In SplashScreen',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0
),),
image: Image.network(
'https://s3.o7planning.com/images/triceratops/image1.png'
),
backgroundColor: Colors.white,
styleTextUnderTheLoader: TextStyle(),
photoSize: 100.0,
onClick: () => print("Flutter o7planning.org"),
loadingText: Text("Loading " + this.loadingPercent.toString() +" %"),
loaderColor: Colors.red
);
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of Home Page"),
),
body: Center(
child: Row (
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
child: Text('Go to Details Page'),
onPressed: () {
Navigator.of(context).pushNamed('/details');
},
),
ElevatedButton(
child: Text('Go to About Page'),
onPressed: () {
Navigator.of(context).pushNamed('/about');
},
),
],
)
),
);
}
}
class DetailsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of Details Page"),
),
body: Center(
child: ElevatedButton(
child: Text('Close'),
onPressed: () {
// Close page and pass a value back to previous page
Navigator.of(context).pop();
},
),
),
backgroundColor: Colors.lightGreen[100],
);
}
}
class AboutPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title of About Page"),
),
body: Center(
child: ElevatedButton(
child: Text('Close'),
onPressed: () {
// Close page
Navigator.of(context).pop();
},
),
),
backgroundColor: Colors.cyan[100],
);
}
}
9. loaderColor
Property loaderColor được sử dụng để sét đặt mầu sắc của loader (CircularProgressIndicator) đang biểu thị tiến trình tải của ứng dụng.
Color loaderColor
11. seconds
Property seconds được sử dụng để sét đặt số giây Splash Screen sẽ xuất hiện trước khi nó được thay thế bởi màn hình chính của ứng dụng.
int seconds
Nếu bạn sét đặt giá trị cho tham số seconds có nghĩa là bạn chọn cách điều hướng dựa trên thời gian, sau khi thời gian hết hạn Splash Screen sẽ được thay thế bởi màn hình chính của ứng dụng.
14. navigateAfterSeconds
dynamic navigateAfterSeconds
Xem giải thích về property này trong ví dụ ở trên.
15. title
title là một widget nằm phía dưới image, trong hầu hết các trường hợp sử dụng nó là một đối tượng Text.
Text title: const Text('')
16. backgroundColor
Property backgroundColor được sử dụng để sét đặt mầu nền cho Splash Screen, giá trị mặc định của nó là Colors.white.
Color backgroundColor: Colors.white
17. styleTextUnderTheLoader
Property styleTextUnderTheLoader không còn được sử dụng.
TextStyle styleTextUnderTheLoader:
const TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black)
18. image
Property image được sử dụng để chỉ định một ảnh xuất hiện trong Splash Screen, nó được đặt trong một CircleAvatar với bán kính là photoSize.
Image image
- Hướng dẫn và ví dụ Flutter Image
- Hướng dẫn và ví dụ Flutter CircleAvatar
19. photoSize
Về mặt cấu trúc image của SplashScreen được đặt trong một CircleAvatar và photoSize là giá trị của bán kính.
double photoSize
20. loadingText
loadingText là một widget nằm ở phía dưới của loader (CircularProgressIndicator) thường được sử dụng để hiển thị thông tin liên quan tới tiến trình tải của ứng dụng.
Text loadingText: const Text("")
21. useLoader
Property useLoader được sử dụng để hiển thị (hoặc không hiển thị) loader (CircularProgressIndicator) trên SplashScreen. Mặc định nó có giá trị true, nghĩa là loader sẽ được hiển thị.
bool useLoader: true
Các hướng dẫn lập trình Flutter
- Hướng dẫn và ví dụ Flutter Column
- Hướng dẫn và ví dụ Flutter Stack
- Hướng dẫn và ví dụ Flutter IndexedStack
- Hướng dẫn và ví dụ Flutter Spacer
- Hướng dẫn và ví dụ Flutter Expanded
- Hướng dẫn và ví dụ Flutter SizedBox
- Hướng dẫn và ví dụ Flutter Tween
- Cài đặt Flutter SDK trên Windows
- Cài đặt Flutter Plugin cho Android Studio
- Tạo ứng dụng Flutter đầu tiên của bạn - Hello Flutter
- Hướng dẫn và ví dụ Flutter Scaffold
- Hướng dẫn và ví dụ Flutter AppBar
- Hướng dẫn và ví dụ Flutter BottomAppBar
- Hướng dẫn và ví dụ Flutter SliverAppBar
- Hướng dẫn và ví dụ Flutter TextButton
- Hướng dẫn và ví dụ Flutter ElevatedButton
- Hướng dẫn và ví dụ Flutter ShapeBorder
- Hướng dẫn và ví dụ Flutter EdgeInsetsGeometry
- Hướng dẫn và ví dụ Flutter EdgeInsets
- Hướng dẫn và ví dụ Flutter CircularProgressIndicator
- Hướng dẫn và ví dụ Flutter LinearProgressIndicator
- Hướng dẫn và ví dụ Flutter Center
- Hướng dẫn và ví dụ Flutter Align
- Hướng dẫn và ví dụ Flutter Row
- Hướng dẫn và ví dụ Flutter SplashScreen
- Hướng dẫn và ví dụ Flutter Alignment
- Hướng dẫn và ví dụ Flutter Positioned
- Hướng dẫn và ví dụ Flutter ListTile
- Hướng dẫn và ví dụ Flutter SimpleDialog
- Hướng dẫn và ví dụ Flutter AlertDialog
- Navigation và Routing trong Flutter
- Hướng dẫn và ví dụ Flutter Navigator
- Hướng dẫn và ví dụ Flutter TabBar
- Hướng dẫn và ví dụ Flutter Banner
- Hướng dẫn và ví dụ Flutter BottomNavigationBar
- Hướng dẫn và ví dụ Flutter FancyBottomNavigation
- Hướng dẫn và ví dụ Flutter Card
- Hướng dẫn và ví dụ Flutter Border
- Hướng dẫn và ví dụ Flutter ContinuousRectangleBorder
- Hướng dẫn và ví dụ Flutter RoundedRectangleBorder
- Hướng dẫn và ví dụ Flutter CircleBorder
- Hướng dẫn và ví dụ Flutter StadiumBorder
- Hướng dẫn và ví dụ Flutter Container
- Hướng dẫn và ví dụ Flutter RotatedBox
- Hướng dẫn và ví dụ Flutter CircleAvatar
- Hướng dẫn và ví dụ Flutter TextField
- Hướng dẫn và ví dụ Flutter IconButton
- Hướng dẫn và ví dụ Flutter FlatButton
- Hướng dẫn và ví dụ Flutter SnackBar
- Hướng dẫn và ví dụ Flutter Drawer
- Ví dụ Flutter Navigator pushNamedAndRemoveUntil
- Hiển thị hình ảnh trên Internet trong Flutter
- Hiển thị ảnh Asset trong Flutter
- Flutter TextInputType các kiểu bàn phím
- Hướng dẫn và ví dụ Flutter NumberTextInputFormatter
- Hướng dẫn và ví dụ Flutter Builder
- Làm sao xác định chiều rộng của Widget cha trong Flutter
- Bài thực hành Flutter thiết kế giao diện màn hình đăng nhập
- Bài thực hành Flutter thiết kế giao diện trang (1)
- Khuôn mẫu thiết kế Flutter với các lớp trừu tượng
- Bài thực hành Flutter thiết kế trang Profile với Stack
- Bài thực hành Flutter thiết kế trang profile (2)
- Hướng dẫn và ví dụ Flutter ListView
- Hướng dẫn và ví dụ Flutter GridView
- Bài thực hành Flutter với gói http và dart:convert (2)
- Bài thực hành Flutter với gói http và dart:convert (1)
- Ứng dụng Flutter Responsive với Menu Drawer
- Flutter GridView với SliverGridDelegate tuỳ biến
- Hướng dẫn và ví dụ Flutter image_picker
- Flutter upload ảnh sử dụng http và ImagePicker
- Hướng dẫn và ví dụ Flutter SharedPreferences
- Chỉ định cổng cố định cho Flutter Web trên Android Studio
- Tạo Module trong Flutter
- Hướng dẫn và ví dụ Flutter SkeletonLoader
- Hướng dẫn và ví dụ Flutter Slider
- Hướng dẫn và ví dụ Flutter Radio
- Bài thực hành Flutter SharedPreferences
- Hướng dẫn và ví dụ Flutter InkWell
- Hướng dẫn và ví dụ Flutter GetX GetBuilder
- Hướng dẫn và ví dụ Flutter GetX obs Obx
- Hướng dẫn và ví dụ Flutter flutter_form_builder
- Xử lý lỗi 404 trong Flutter GetX
- Ví dụ đăng nhập và đăng xuất với Flutter Getx
- Hướng dẫn và ví dụ Flutter multi_dropdown
Show More