Flutter中使用GetX从0到1搭建自已的项目框架(一)

3,151 阅读5分钟

GetX是一个用于Flutter应用程序开发的全能工具包,它提供了一系列功能和工具,包括状态管理、路由导航、依赖注入和国际化等。GetX的目标是简化Flutter应用程序的开发过程,并提供快速而简洁的解决方案。

GetX具有以下特点和优势:

  1. 简单易用:GetX使用简洁的语法和链式调用,使得代码编写更加直观和简单。它减少了样板代码的数量,提高了开发效率。

  2. 强大的状态管理:GetX提供了强大而灵活的状态管理解决方案。它支持可变状态、不可变状态和响应式状态,并提供了方便的方法来管理和更新状态。GetX的状态管理功能可以轻松处理复杂的应用程序状态。

  3. 灵活的路由导航:GetX提供了灵活且易于使用的路由导航功能。它支持命名路由、参数传递和页面过渡动画,使得页面导航变得简单而灵活。

  4. 依赖注入:GetX集成了依赖注入功能,使得管理和访问应用程序中的依赖关系变得简单。它提供了方便的方法来注册和获取依赖项,支持单例和懒加载等特性。

  5. 其他功能:GetX还提供了其他有用的功能,如国际化支持、对话框管理、SnackBar管理等。这些功能使得开发者可以更加方便地处理应用程序中的常见任务和功能。

总的来说,GetX是一个功能强大且易于使用的Flutter工具包,它简化了应用程序开发过程,并提供了丰富的功能和工具来加速开发。无论是小型项目还是大型应用程序,GetX都是一个值得尝试的工具,可以提升开发效率并改善代码的可维护性。

开源项目地址:github.com/jonataslaw/…

getx.png 如果开发一般的小项目可以直接使用官方的建议按照文档一步一步的接入即可,但是对于企业级的项目时要考虑的因素就比较多了,比如后期作者不维护了,自已怎样在其上二次开发;还有如果后期想要替换这个框架应该怎么做?

下面我们就来看看如何用GetX从0到1搭建一个企业级的应用框架:

1.先来看一下我们是如何将GetX引用到项目中的:

example/lib/main.dart直接引用 EdenMaterialWrapper image.png

void main() {
  runApp(
    EdenMaterialWrapper(
      logTag: "Example",
      enableLog: true,
      initialRoute: Routes.app.root,
      unknownRoute: Routes.app.unknownRoute,
      getPages: Routes.getPages(),
      theme: EdenThemeData.lightThemeData(),
      initialBinding: ServiceBindings(),
      splashBuilder: (context, child) {
        // final botToastBuilder = BotToastInit();
        // child = botToastBuilder(context, child);

        return FutureBuilder<void>(
          key: const ValueKey('initFuture'),
          future: Get.find<SplashService>().init(),
          builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
            print("snapshot=${snapshot.connectionState}");
            if (snapshot.connectionState == ConnectionState.done) {
              return child ?? const SizedBox.shrink();
            }
            return SplashView();
          },
        );
      },
    ),
  );
  EdenThemeData.systemUiOverlay();
}

实现initialRoute

initialRoute: Routes.app.root

AppRoute用来管理app根模块相关的路由信息

class AppRoute extends EdenBaseRoute {
  @override
  String get prefix => "/app";

  String get root => "/root";

  String get home => prefix + "/home";
  String get message => prefix + "/message";
  String get account => prefix + "/account";

  ///
  String get login => prefix + "/login";
  String get accountProfile => prefix + "/account/profile";
  String get proxy => prefix + "/proxy";

  /// 找不到页面
  String get error404 => prefix + "/error/unknown404";

  GetPage get unknownRoute => routePage(
      name: error404,
      page: () => ErrorPage(),
      bindingsBuilder: () {
        Get.lazyPut(() => ErrorController());
      });
  @override
  List<GetPage> getRoutePages() {
    return [
      routePage(
        name: root,
        page: () => AppComponent(),
        bindings: [
          AppBindings(),
        ],
        // middlewares: [AuthMiddleware(priority: 0)],
        bindingsBuilder: () {
          // Get.lazyPut(() => AppController());
        },
        children: [
          routePage(
            name: home,
            page: () => HomeIndex(),
            bindingsBuilder: () {
              // Get.lazyPut(() => HomeController());
            },
          ),
          routePage(
            name: message,
            page: () => MessagePage(),
            bindingsBuilder: () {
              // Get.lazyPut(() => MessageController());
            },
          ),
          routePage(
            name: account,
            page: () => AccountPage(),
            bindingsBuilder: () {},
          ),
        ],
      ),
      routePage(
        name: proxy,
        page: () => ProxySettingPage(),
        bindingsBuilder: () {},
      ),

      /// login
      routePage(
        name: login,
        page: () => LoginPage(),
        bindingsBuilder: () {
          //dao
          Get.lazyPut<ILoginProvider>(() => LoginProvider());

          //service
          Get.lazyPut<ILoginRespository>(
              () => LoginRespositoryImpl(provider: Get.find()));
          //controller
          Get.lazyPut(() => LoginController(loginRespository: Get.find()));
        },
      ),
      //profile
      routePage(
        name: accountProfile,
        page: () => ProfilePage(),
        bindingsBuilder: () {
          Get.lazyPut(() => ProfileController());
        },
      ),
      routePage(
          name: error404,
          page: () => ErrorPage(),
          bindingsBuilder: () {
            Get.lazyPut(() => ErrorController());
          })
    ];
  }
}

AppComponent 项目的根组件

class AppComponent extends EdenNavBaseWidget<AppController> {
  @override
  String toolbarTitle() {
    return "";
  }

  @override
  bool hideToolbar() {
    return true;
  }

  @override
  List<BottomNavigationBarItem> renderBarItems() {
    return [
      const BottomNavigationBarItem(
        label: "Home",
        icon: Icon(
          Icons.home,
        ),
      ),
      const BottomNavigationBarItem(
        label: "Message",
        icon: Icon(
          Icons.message,
        ),
      ),
      const BottomNavigationBarItem(
        label: "Me",
        icon: Icon(
          Icons.account_circle,
        ),
      ),
    ];
  }

  @override
  List<Widget> renderPages() {
    return [
      EdenKeepAliveWrapper(child: HomeIndex()),
      EdenKeepAliveWrapper(child: MessagePage()),
      EdenKeepAliveWrapper(child: AccountPage()),
    ];
  }
}

image.png

EdenKeepAliveWrapper中使用了AutomaticKeepAliveClientMixin是一个Flutter mixin(混入),用于保持小部件的生命周期。它用于维持小部件在页面切换或滚动时的状态,以便保持其状态不被销毁。


class EdenKeepAliveWrapper extends StatefulWidget {
  final Widget child;
  const EdenKeepAliveWrapper({Key? key, required this.child}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _EdenKeepAliveWrapperState();
}

class _EdenKeepAliveWrapperState extends State<EdenKeepAliveWrapper>
    with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return widget.child;
  }
}

EdenMaterialWrapper具体的实现:

image.png

import 'package:flutter_eden/eden.dart';
import 'package:flutter_eden/src/core/logger/eden_logger.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

class EdenMaterialWrapper extends StatelessWidget {
  final bool? enableLog;
  final String? logTag;
  final Widget? home;
  final String? initialRoute;
  final List<GetPage>? getPages;
  final GetPage? unknownRoute;
  final Size? designSize;
  final ThemeData? theme;
  final Bindings? initialBinding;
  final TransitionBuilder? splashBuilder;
  final Iterable<Locale>? supportedLocales;
  final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
  final ValueChanged<Routing?>? routingCallback;
  const EdenMaterialWrapper({
    Key? key,
    this.enableLog,
    this.logTag,
    this.home,
    this.initialRoute,
    this.designSize,
    this.unknownRoute,
    this.getPages,
    this.theme,
    this.initialBinding,
    this.splashBuilder,
    this.supportedLocales,
    this.localizationsDelegates,
    this.routingCallback,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return EdenLayoutWrapper(
      designSize: designSize ?? EdenLayoutUtils.designSize,
      builder: (context, child) {
        return RefreshConfiguration(
          headerBuilder: () => Theme(
              data: Theme.of(context).copyWith(
                cupertinoOverrideTheme: const CupertinoThemeData(
                  brightness: Brightness.light,
                ),
              ),
              child: const WaterDropHeader()),
          // 配置默认头部指示器,假如你每个页面的头部指示器都一样的话,你需要设置这个
          footerBuilder: () => const ClassicFooter(),
          // 配置默认底部指示器
          headerTriggerDistance: 80,
          // 头部触发刷新的越界距离
          springDescription:
              const SpringDescription(stiffness: 170, damping: 16, mass: 1.9),
          // 自定义回弹动画,三个属性值意义请查询flutter api
          maxOverScrollExtent: 100,
          //头部最大可以拖动的范围,如果发生冲出视图范围区域,请设置这个属性
          maxUnderScrollExtent: 0,
          // 底部最大可以拖动的范围
          enableScrollWhenRefreshCompleted: true,
          //这个属性不兼容PageView和TabBarView,如果你特别需要TabBarView左右滑动,你需要把它设置为true
          enableLoadingWhenFailed: true,
          //在加载失败的状态下,用户仍然可以通过手势上拉来触发加载更多
          hideFooterWhenNotFull: false,
          // Viewport不满一屏时,禁用上拉加载更多功能
          enableBallisticLoad: true,
          child: materialApp,
        );
      },
    );
  }

  ///
  Widget get materialApp => GetMaterialApp(
        enableLog: enableLog ?? true,
        defaultTransition: Transition.cupertino,
        home: home,
        initialRoute: initialRoute,
        getPages: getPages ?? [],
        unknownRoute: unknownRoute,
        theme: theme ?? EdenThemeData.darkThemeData(),
        initialBinding: initialBinding,
        logWriterCallback: (String text, {bool isError = false}) {
          return Logger.edenWrite(text, logTag: logTag, isError: isError);
        },
        routingCallback: routingCallback ??
            (route) {
              closeAllSnackbars();
            },
        navigatorObservers: [BotToastNavigatorObserver()],
        localizationsDelegates: [
          ...localizationsDelegates ?? [],
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
          RefreshLocalizations.delegate,
        ],
        supportedLocales: [
          ...supportedLocales ?? [],
          const Locale.fromSubtags(
            languageCode: 'zh',
            scriptCode: 'Hans',
            countryCode: 'CN',
          ),
        ],
        builder: (BuildContext context, Widget? child) {
          final botToastBuilder = BotToastInit();
          child = botToastBuilder(context, child);

          return splashBuilder == null ? child : splashBuilder!(context, child);
        },
      );
}

通过对GetMaterialApp的封装,在项目中可轻松实现其必要的方法来快速搭建自已的项目,使团队开发和后期维护变得更加高效。

由于篇幅原因今天就分享到这里,后续会连载!

如果您对此项目感兴趣可以加入一起维护开发!

开源项目地址:github.com/rebelning/f…

希望对您有所帮助谢谢!!!