Quellcode durchsuchen

[feat]创建tab

Destiny vor 1 Jahr
Ursprung
Commit
e644ec162e

BIN
assets/images/icon_tab_home_selected.webp


BIN
assets/images/icon_tab_home_unselect.webp


BIN
assets/images/icon_tab_more_selected.webp


BIN
assets/images/icon_tab_more_unselect.webp


+ 3 - 1
ios/Runner.xcodeproj/project.pbxproj

@@ -114,7 +114,6 @@
 				3180A9BA72D5F8BEB0749A22 /* Pods-RunnerTests.release.xcconfig */,
 				12CACE75FFFEF9D73E59759E /* Pods-RunnerTests.profile.xcconfig */,
 			);
-			name = Pods;
 			path = Pods;
 			sourceTree = "<group>";
 		};
@@ -489,6 +488,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				DEVELOPMENT_TEAM = WU64MT4KXD;
 				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = Runner/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -671,6 +671,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				DEVELOPMENT_TEAM = WU64MT4KXD;
 				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = Runner/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -693,6 +694,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				DEVELOPMENT_TEAM = WU64MT4KXD;
 				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = Runner/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = (

+ 30 - 0
lib/base/base_controller.dart

@@ -0,0 +1,30 @@
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+
+class BaseController extends GetxController {
+  Map? parameters;
+
+  @override
+  void onInit() {
+    super.onInit();
+    _initParameters();
+  }
+
+  void _initParameters() {
+    var getParameters = Get.parameters;
+    var getArguments = Get.arguments;
+    parameters ??= <dynamic, dynamic>{};
+    parameters?.addAll(getParameters);
+    if (getArguments != null && getArguments is Map) {
+      parameters?.addAll(getArguments);
+    }
+  }
+
+  /// 隐藏键盘
+  void hideKeyboard(BuildContext context) {
+    FocusScopeNode currentFocus = FocusScope.of(context);
+    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
+      FocusManager.instance.primaryFocus!.unfocus();
+    }
+  }
+}

+ 52 - 0
lib/base/base_page.dart

@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'base_view.dart';
+
+abstract class BasePage<T extends GetxController> extends BaseView<T> {
+  const BasePage({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return AnnotatedRegion<SystemUiOverlayStyle>(
+      value: SystemUiOverlayStyle.light.copyWith(
+        statusBarColor: Colors.transparent,
+        statusBarBrightness: _getStatusBarDarkFont(),
+        statusBarIconBrightness: _getStatusIconBarDarkFont(),
+        systemNavigationBarColor: navigationBarColor(),
+        systemNavigationBarIconBrightness: _getNavigationBarDarkFont(),
+      ),
+      child: buildBoot(immersive()
+          ? buildBody(context)
+          : SafeArea(child: buildBody(context))),
+    );
+  }
+
+  Brightness _getStatusBarDarkFont() {
+    return statusBarDarkFont() ? Brightness.light : Brightness.dark;
+  }
+
+  Brightness _getStatusIconBarDarkFont() {
+    return statusBarDarkFont() ? Brightness.dark : Brightness.light;
+  }
+
+  Brightness _getNavigationBarDarkFont() {
+    return statusBarDarkFont() ? Brightness.dark : Brightness.light;
+  }
+
+  bool statusBarDarkFont() {
+    return true;
+  }
+
+  bool navigationBarDarkFont() {
+    return true;
+  }
+
+  bool immersive() {
+    return false;
+  }
+
+  Color navigationBarColor() {
+    return Colors.transparent;
+  }
+}

+ 36 - 0
lib/base/base_view.dart

@@ -0,0 +1,36 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+abstract class BaseView<T extends GetxController> extends GetView<T> {
+  const BaseView({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return buildBoot(buildBody(context));
+  }
+
+  Widget buildBoot(Widget child) {
+    return GestureDetector(
+        onTap: () {
+          backgroundOnTapEvent();
+        },
+        child: Container(
+          height: viewHeight(),
+          color: backgroundColor(),
+          child: child,
+        ));
+  }
+
+  Widget buildBody(BuildContext context);
+
+  // 点击空白处
+  void backgroundOnTapEvent() {}
+
+  Color backgroundColor() {
+    return Colors.white;
+  }
+
+  double? viewHeight() {
+    return null;
+  }
+}

+ 43 - 107
lib/main.dart

@@ -1,4 +1,10 @@
+import 'package:clean/resource/colors.gen.dart';
+import 'package:clean/router/app_pages.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get_navigation/src/root/get_material_app.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
 
 void main() {
   runApp(const MyApp());
@@ -7,119 +13,49 @@ void main() {
 class MyApp extends StatelessWidget {
   const MyApp({super.key});
 
-  // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
-    return MaterialApp(
-      title: 'Flutter Demo',
-      theme: ThemeData(
-        // This is the theme of your application.
-        //
-        // TRY THIS: Try running your application with "flutter run". You'll see
-        // the application has a purple toolbar. Then, without quitting the app,
-        // try changing the seedColor in the colorScheme below to Colors.green
-        // and then invoke "hot reload" (save your changes or press the "hot
-        // reload" button in a Flutter-supported IDE, or press "r" if you used
-        // the command line to start the app).
-        //
-        // Notice that the counter didn't reset back to zero; the application
-        // state is not lost during the reload. To reset the state, use hot
-        // restart instead.
-        //
-        // This works for code too, not just values: Most code changes can be
-        // tested with just a hot reload.
-        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
-        useMaterial3: true,
-      ),
-      home: const MyHomePage(title: 'Flutter Demo Home Page'),
+    return ScreenUtilInit(
+      designSize: const Size(360, 800),
+      builder: (_, child) {
+        return _buildMaterialApp();
+      },
     );
   }
-}
-
-class MyHomePage extends StatefulWidget {
-  const MyHomePage({super.key, required this.title});
-
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
-
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
-
-  final String title;
-
-  @override
-  State<MyHomePage> createState() => _MyHomePageState();
-}
-
-class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
-  }
 
-  @override
-  Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
-    return Scaffold(
-      appBar: AppBar(
-        // TRY THIS: Try changing the color here to a specific color (to
-        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
-        // change color while the other colors stay the same.
-        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
-      ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
-        child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          //
-          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
-          // action in the IDE, or press "p" in the console), to see the
-          // wireframe for each widget.
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text(
-              'You have pushed the button this many times:',
-            ),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headlineMedium,
-            ),
-          ],
+  _buildMaterialApp() {
+    return RefreshConfiguration(
+      headerBuilder: () =>
+      const MaterialClassicHeader(color: ColorName.colorPrimary),
+      // footerBuilder: () => ClassicFooter(
+      //   canLoadingText: StringName.loadingMore,
+      //   idleText: StringName.loadPullUp,
+      //   loadingText: StringName.loadingTxt,
+      //   noDataText: StringName.loadNoData,
+      //   failedText: StringName.loadFailed,
+      // ),
+      child: GetMaterialApp(
+        onGenerateTitle: (_) => "clean",
+        getPages: AppPage.pages,
+        initialRoute: RoutePath.mainTab,
+        initialBinding: AppBinding(),
+        theme: ThemeData(
+          useMaterial3: true,
+          textSelectionTheme: const TextSelectionThemeData(
+            cursorColor: ColorName.colorPrimary, // 设置默认光标颜色
+            selectionHandleColor: ColorName.colorPrimary, // 设置光标下面水滴的颜色
+          ),
         ),
+        navigatorObservers: [FlutterSmartDialog.observer],
+        builder: FlutterSmartDialog.init(),
+        supportedLocales: const [
+          Locale('zh', 'CN'), // 支持的语言和地区
+        ],
+        // 你的翻译
+        locale: const Locale('zh', 'CN'),
+        // 将会按照此处指定的语言翻译 添加一个回调语言选项,以备上面指定的语言翻译不存在
+        fallbackLocale: const Locale('zh', 'CN'),
       ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: const Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
     );
   }
-}
+}

+ 5 - 0
lib/module/home/home_controller.dart

@@ -0,0 +1,5 @@
+import 'package:clean/base/base_controller.dart';
+
+class HomeController extends BaseController {
+
+}

+ 24 - 0
lib/module/home/home_view.dart

@@ -0,0 +1,24 @@
+
+import 'dart:ui';
+
+import 'package:clean/base/base_view.dart';
+import 'package:clean/module/home/home_controller.dart';
+import 'package:flutter/Material.dart';
+
+class HomePage extends BaseView<HomeController> {
+
+  const HomePage({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.white;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    // TODO: implement buildBody
+    return Container(
+      color: Colors.black,
+    );
+  }
+}

+ 43 - 0
lib/module/main/main_controller.dart

@@ -0,0 +1,43 @@
+import 'dart:ui';
+
+import 'package:get/get.dart';
+
+import '../../base/base_controller.dart';
+import '../../resource/assets.gen.dart';
+
+class MainController extends BaseController {
+
+  @override
+  void onInit() {
+    super.onInit();
+  }
+
+  final List<TabBean> tabBeans = [
+    TabBean(
+        Assets.images.iconTabHomeUnselect.path,
+        Assets.images.iconTabHomeSelected.path),
+    TabBean(
+        Assets.images.iconTabMoreUnselect.path,
+        Assets.images.iconTabMoreSelected.path),
+  ];
+
+  final _currentIndex = 0.obs;
+
+  int get currentIndex => _currentIndex.value;
+
+  void changeIndex(int index) {
+    _currentIndex.value = index;
+  }
+
+  void updateIndex(int index) {
+    _currentIndex.value = index;
+  }
+}
+
+
+class TabBean {
+  final String normalIcon;
+  final String selectedIcon;
+
+  TabBean(this.normalIcon, this.selectedIcon);
+}

+ 129 - 0
lib/module/main/main_view.dart

@@ -0,0 +1,129 @@
+import 'package:clean/module/more/more_view.dart';
+import 'package:clean/utils/expand.dart';
+import 'package:convex_bottom_bar/convex_bottom_bar.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+
+import '../../base/base_page.dart';
+import '../../resource/assets.gen.dart';
+import '../../router/app_pages.dart';
+import '../home/home_view.dart';
+import 'main_controller.dart';
+import 'package:flutter/Material.dart';
+import 'package:get/get.dart';
+
+class MainTabPage extends BasePage<MainController> {
+  MainTabPage({super.key});
+
+  final pages = [
+    const HomePage(),
+    const MorePage(),
+  ];
+
+  static void start() {
+    Get.offNamed(RoutePath.mainTab, arguments: {});
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return PopScope(
+      canPop: false,
+      child: Scaffold(
+        bottomNavigationBar: buildBottomAppBar(),
+        drawerEdgeDragWidth: 0,
+        body: Obx(() {
+          return pages[controller.currentIndex];
+        }),
+      ),
+    );
+  }
+
+  Widget buildBottomAppBar() {
+    return Container(
+      // height: 100.h,
+      // decoration: BoxDecoration(
+      //   color: "#0F0F16".color,
+      //   boxShadow: [
+      //     BoxShadow(
+      //       color: Colors.black.withOpacity(0.1),
+      //       spreadRadius: 1,
+      //       blurRadius: 10,
+      //       offset: const Offset(0, 0),
+      //     ),
+      //   ],
+      //   borderRadius: BorderRadius.only(
+      //     topLeft: Radius.circular(20.r),
+      //     topRight: Radius.circular(20.r),
+      //   ),
+      // ),
+      child: ConvexAppBar(
+        height: 80.h,
+        items: [
+          TabItem(icon: Assets.images.iconTabHomeUnselect.image(width: 24.w, height: 24.w), activeIcon: Assets.images.iconTabHomeSelected.image(width: 66.w, height: 66.w)),
+          TabItem(icon: Assets.images.iconTabMoreUnselect.image(width: 24.w, height: 24.w), activeIcon: Assets.images.iconTabMoreSelected.image(width: 66.w, height: 66.w)),
+        ],
+        onTap: (int i) {
+          if (controller.currentIndex != i) {
+            controller.changeIndex(i);
+          }
+        },
+      )
+
+
+      // BottomAppBar(
+      //   color: Colors.transparent,
+      //   height: 100.h,
+      //   padding: EdgeInsets.zero,
+      //   shape: CircularNotchedRectangle(),
+      //   child: Flex(
+      //     direction: Axis.horizontal,
+      //     mainAxisAlignment: MainAxisAlignment.spaceAround,
+      //     children: [
+      //       Expanded(flex: 1, child: bottomAppBarItem(0)),
+      //       const Expanded(flex: 1, child: SizedBox()),
+      //       Expanded(flex: 1, child: bottomAppBarItem(1))
+      //     ],
+      //   ),
+      // ),
+    );
+  }
+
+  Widget bottomAppBarItem(int index) {
+    return Obx(() {
+      TabBean tabBean = controller.tabBeans[index];
+      String imagePath = controller.currentIndex == index
+          ? tabBean.selectedIcon
+          : tabBean.normalIcon;
+
+      return GestureDetector(
+        behavior: HitTestBehavior.opaque,
+        onTap: () {
+          if (controller.currentIndex != index) {
+            controller.changeIndex(index);
+          }
+        },
+        child: Container(
+          child: SizedBox(
+            height: 100.h,
+            child: Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                controller.currentIndex == index
+                    ? Image.asset(
+                  imagePath,
+                  width: 66.w,
+                  height: 66.w,
+                )
+                    : Image.asset(
+                  imagePath,
+                  width: 24.w,
+                  height: 24.w,
+                ),
+              ],
+            ),
+          ),
+        ),
+
+      );
+    });
+  }
+}

+ 5 - 0
lib/module/more/more_controller.dart

@@ -0,0 +1,5 @@
+import 'package:clean/base/base_controller.dart';
+
+class MoreController extends BaseController {
+
+}

+ 21 - 0
lib/module/more/more_view.dart

@@ -0,0 +1,21 @@
+import 'dart:ui';
+
+import 'package:clean/base/base_view.dart';
+import 'package:clean/module/more/more_controller.dart';
+import 'package:flutter/Material.dart';
+
+class MorePage extends BaseView<MoreController> {
+
+  const MorePage({super.key});
+
+  @override
+  Color backgroundColor() {
+    return Colors.white;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    // TODO: implement buildBody
+    throw UnimplementedError();
+  }
+}

+ 31 - 0
lib/router/app_pages.dart

@@ -0,0 +1,31 @@
+import 'package:clean/module/main/main_view.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+import 'package:get/get_instance/src/bindings_interface.dart';
+
+import '../module/main/main_controller.dart';
+
+abstract class AppPage {
+  static final pages = <GetPage>[
+    ...generalPages,
+  ];
+}
+
+abstract class RoutePath {
+  static const mainTab = '/mainTab';
+}
+
+class AppBinding extends Bindings {
+  @override
+  void dependencies() {
+    lazyPut(() => MainController());
+  }
+
+  void lazyPut<S>(InstanceBuilderCallback<S> builder) {
+    Get.lazyPut(builder, fenix: true);
+  }
+}
+
+final generalPages = [
+  GetPage(name: RoutePath.mainTab, page: () => MainTabPage()),
+];

+ 13 - 0
lib/utils/expand.dart

@@ -0,0 +1,13 @@
+import 'dart:ui';
+
+extension HexColor on String {
+  Color get color => toColor();
+
+  Color toColor() {
+    String hex = replaceAll('#', '');
+    if (hex.length == 6) {
+      hex = 'FF$hex';
+    }
+    return Color(int.parse(hex, radix: 16));
+  }
+}

+ 24 - 0
pubspec.lock

@@ -182,6 +182,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.1.2"
+  convex_bottom_bar:
+    dependency: "direct main"
+    description:
+      name: convex_bottom_bar
+      sha256: ebf0f3deb1e8e99374d844fee7485d2980ec502dedfaad395c12118477933aef
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.2.0"
   crypto:
     dependency: transitive
     description:
@@ -315,6 +323,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.9.3"
+  flutter_smart_dialog:
+    dependency: "direct main"
+    description:
+      name: flutter_smart_dialog
+      sha256: d7b915461fdc9bb8111d23a709b4ce910dbc4b9bef0fbd941655f74bf7de09a6
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "4.9.8+5"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -645,6 +661,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.4.0"
+  pull_to_refresh:
+    dependency: "direct main"
+    description:
+      name: pull_to_refresh
+      sha256: bbadd5a931837b57739cf08736bea63167e284e71fb23b218c8c9a6e042aad12
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.0.0"
   retrofit:
     dependency: transitive
     description:

+ 8 - 1
pubspec.yaml

@@ -50,6 +50,14 @@ dependencies:
   #权限申请
   permission_handler: ^11.3.1
 
+  # 弹窗
+  flutter_smart_dialog: ^4.9.8
+
+  #上、下拉刷新
+  pull_to_refresh: ^2.0.0
+
+  convex_bottom_bar: ^3.2.0
+
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^1.0.8
@@ -93,7 +101,6 @@ flutter:
   # the material Icons class.
   uses-material-design: true
 
-
   assets:
     - assets/images/