本节目标
- 嵌套导航
- 多语言
- 主题
- 3 UI 组件
- Snackbar
- Dialog
- BottomSheet
视频
https://www.bilibili.com/video/BV1RU4y1t7DD/
代码
https://github.com/ducafecat/getx_quick_start
参考
正文
嵌套导航
几个 Navigator
widget ,并排或者嵌套,他们是通过属性 key
来区分的,具体去哪里是通过 onGenerateRoute
实现的,在 getx 中 我们要把业务写到 controller
中,状态切换用 Obx
控制 BottomNavigationBar
,代码如下。
- lib/pages/nested_navigation/controller.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class NestedController extends GetxController { static NestedController get to => Get.find();
var currentIndex = 0.obs;
final pages = <String>['/list', '/detail', '/login'];
void changePage(int index) { currentIndex.value = index; Get.toNamed(pages[index], id: 1); }
Route? onGenerateRoute(RouteSettings settings) { if (settings.name == '/login') return GetPageRoute( settings: settings, page: () => LoginView(), transition: Transition.topLevel, ); else if (settings.name == '/list') return GetPageRoute( settings: settings, page: () => ListIndexView(), transition: Transition.rightToLeftWithFade, ); else if (settings.name == '/detail') return GetPageRoute( settings: settings, page: () => DetailView(), transition: Transition.fadeIn, );
return null; } }
|
- lib/pages/nested_navigation/binding.dart
1 2 3 4 5 6
| class NestedBinding extends Bindings { @override void dependencies() { Get.lazyPut(() => NestedController()); } }
|
- lib/pages/nested_navigation/index.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class NestedNavView extends GetView<NestedController> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("嵌套路由"), ), body: Container( color: Colors.amber, child: Column( children: [ Container( child: Text("占位条"), height: 100, ), SizedBox( height: 300, child: Navigator( key: Get.nestedKey(1), initialRoute: '/list', onGenerateRoute: controller.onGenerateRoute, ), ), ], ), ), bottomNavigationBar: Obx( () => BottomNavigationBar( items: const <BottomNavigationBarItem>[ BottomNavigationBarItem( icon: Icon(Icons.list), label: '列表', ), BottomNavigationBarItem( icon: Icon(Icons.details), label: '详情', ), BottomNavigationBarItem( icon: Icon(Icons.login), label: '登录', ), ], currentIndex: controller.currentIndex.value, selectedItemColor: Colors.pink, onTap: controller.changePage, ), ), ); } }
|
- lib/common/routes/app_pages.dart
1 2 3 4 5
| GetPage( name: AppRoutes.NestedNavigator, page: () => NestedNavView(), binding: NestedBinding(), ),
|
多语言
文件名格式 [国家]_[语言].dart
lib/common/lang/en_US.dart
1 2 3 4
| const Map<String, String> en_US = { 'title': 'This is Title!', 'login': 'logged in as @name with email @email', };
|
lib/common/lang/zh_Hans.dart
1 2 3 4
| const Map<String, String> zh_Hans = { 'title': '这是标题', 'login': '登录用户 @name,邮箱账号 @email', };
|
lib/common/lang/zh_HK.dart
1 2 3 4
| const Map<String, String> zh_HK = { 'title': '這是標題', 'login': '登錄用戶 @name,郵箱賬號 @email', };
|
lib/common/lang/translation_service.dart
1 2 3 4 5 6 7 8 9 10
| class TranslationService extends Translations { static Locale? get locale => Get.deviceLocale; static final fallbackLocale = Locale('en', 'US'); @override Map<String, Map<String, String>> get keys => { 'en_US': en_US, 'zh_Hans': zh_Hans, 'zh_HK': zh_HK, }; }
|
lib/main.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return GetMaterialApp( ...
locale: TranslationService.locale, fallbackLocale: TranslationService.fallbackLocale, translations: TranslationService(), ); } }
|
locale
当前系统语言
fallbackLocale
如果找不到对应字典,默认值
translations
字典列表
采用扩展操作符方式调用显示,点赞 xxx.tr,
切换语言 Get.updateLocale
1 2 3 4 5 6
| "title -> " + 'title'.tr,
......
var locale = Locale('zh', 'HK'); Get.updateLocale(locale);
|
lib/pages/lang/index.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class LangView extends StatelessWidget { const LangView({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("多语言"), ), body: Center( child: Column( children: [ Text( "title -> " + 'title'.tr, style: TextStyle(fontSize: 24), ), Divider(), Text( "login -> " + 'login'.trParams( {'name': 'ducafecat', 'email': 'ducafecat@gmail.com'})!, style: TextStyle(fontSize: 24), ), Divider(), ListTile( title: Text("切换语言"), subtitle: Text('zh-HK'), onTap: () { var locale = Locale('zh', 'HK'); Get.updateLocale(locale); }, ), ListTile( title: Text("切换语言"), subtitle: Text('zh-Hans'), onTap: () { var locale = Locale('zh', 'Hans'); Get.updateLocale(locale); }, ), ListTile( title: Text("切换语言"), subtitle: Text('en-US'), onTap: () { var locale = Locale('en', 'US'); Get.updateLocale(locale); }, ), ], ), ), ); } }
|
主题
直接 Get.changeTheme
切换 ThemeData
数据。
1 2 3 4
| onTap: () { Get.changeTheme( Get.isDarkMode ? ThemeData.light() : ThemeData.dark()); },
|
- lib/pages/theme/index.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class ThemeView extends StatelessWidget { const ThemeView({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("主题"), ), body: Center( child: Column( children: [ Container( height: 100, child: Align( alignment: Alignment.center, child: Text( "是否黑色主题 -> " + Get.isDarkMode.toString(), style: TextStyle(fontSize: 24), )), ), Divider(), ListTile( title: Text("切换主题"), subtitle: Text('Get.changeTheme'), onTap: () { Get.changeTheme( Get.isDarkMode ? ThemeData.light() : ThemeData.dark()); }, ), ], ), ), ); } }
|
Snackbar
1 2 3 4
| onTap: () => Get.snackbar( "标题", "消息", ),
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| void snackbar<T>( String title, String message, { Color? colorText, Duration? duration,
bool instantInit = true, SnackPosition? snackPosition, Widget? titleText, Widget? messageText, Widget? icon, bool? shouldIconPulse, double? maxWidth, EdgeInsets? margin, EdgeInsets? padding, double? borderRadius, Color? borderColor, double? borderWidth, Color? backgroundColor, Color? leftBarIndicatorColor, List<BoxShadow>? boxShadows, Gradient? backgroundGradient, TextButton? mainButton, OnTap? onTap, bool? isDismissible, bool? showProgressIndicator, SnackDismissDirection? dismissDirection, AnimationController? progressIndicatorController, Color? progressIndicatorBackgroundColor, Animation<Color>? progressIndicatorValueColor, SnackStyle? snackStyle, Curve? forwardAnimationCurve, Curve? reverseAnimationCurve, Duration? animationDuration, double? barBlur, double? overlayBlur, SnackbarStatusCallback? snackbarStatus, Color? overlayColor, Form? userInputForm, }) async {
|
Dialog
1 2 3 4 5 6 7 8 9 10 11 12 13
| onTap: () => Get.defaultDialog( title: "标题", content: Column( children: [ Text("第1行"), Text("第2行"), Text("第3行"), ], ), textConfirm: "确认", textCancel: "取消", onConfirm: () => Get.back(), ),
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Future<T?> defaultDialog<T>({ String title = "Alert", TextStyle? titleStyle, Widget? content, VoidCallback? onConfirm, VoidCallback? onCancel, VoidCallback? onCustom, Color? cancelTextColor, Color? confirmTextColor, String? textConfirm, String? textCancel, String? textCustom, Widget? confirm, Widget? cancel, Widget? custom, Color? backgroundColor, bool barrierDismissible = true, Color? buttonColor, String middleText = "Dialog made in 3 lines of code", TextStyle? middleTextStyle, double radius = 20.0, List<Widget>? actions,
WillPopCallback? onWillPop, }) {
|
BottomSheet
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| onTap: () => Get.bottomSheet( Container( height: 200, color: Colors.white, child: Column( children: [ Text("第1行"), Text("第2行"), Text("第3行"), ], ), ), ), ),
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| extension ExtensionBottomSheet on GetInterface { Future<T?> bottomSheet<T>( Widget bottomsheet, { Color? backgroundColor, double? elevation, bool persistent = true, ShapeBorder? shape, Clip? clipBehavior, Color? barrierColor, bool? ignoreSafeArea, bool isScrollControlled = false, bool useRootNavigator = false, bool isDismissible = true, bool enableDrag = true, RouteSettings? settings, Duration? enterBottomSheetDuration, Duration? exitBottomSheetDuration, }) {
|
© 猫哥
https://ducafecat.tech/
https://github.com/ducafecat
往期
开源
GetX Quick Start
https://github.com/ducafecat/getx_quick_start
新闻客户端
https://github.com/ducafecat/flutter_learn_news
strapi 手册译文
https://getstrapi.cn
微信讨论群 ducafecat
系列集合
译文
https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/
Dart 编程语言基础
https://space.bilibili.com/404904528/channel/detail?cid=111585
Flutter 零基础入门
https://space.bilibili.com/404904528/channel/detail?cid=123470
Flutter 实战从零开始 新闻客户端
https://space.bilibili.com/404904528/channel/detail?cid=106755
Flutter 组件开发
https://space.bilibili.com/404904528/channel/detail?cid=144262
Flutter Bloc
https://space.bilibili.com/404904528/channel/detail?cid=177519
Flutter Getx4
https://space.bilibili.com/404904528/channel/detail?cid=177514
Docker Yapi
https://space.bilibili.com/404904528/channel/detail?cid=130578
邮箱 ducafecat@gmail.com / 微信 ducafecat / 留言板 disqus