flutter状态库到底用哪个!

初入flutter,状态库到底用哪个!

我之前没写过flutter,进了新公司要求使用flutter。由于对flutetr的生态不熟悉,常常感到疑惑,我到底该用哪个库。其中最有争议的就是状态库的选择,在查找了诸多资料和群里询问交流后,写下了这篇文章,对我所接触到的一些flutter库进行调研。

状态库到底用getx还是provider,令刚开始接触flutter的我甚为纠结,provider是官方推荐,但感觉不是很好用,getx网友褒贬不一,还说什么初学者用了getx就废了(笑哭),让我不知该如何是好,因为我都不熟悉,后来又了解到provider的作者还写了一个更好用的库riverpod,去了解了一下,然后选择就变成了getx or riverpod。

由于一时之间不能决定,于是我决定分开两个分支来写,一个使用riverpod + flutter_hooks,一个用getx。

使用riverpod + flutter_hooks

需要安装的依赖:flutter pub add hooks_riverpod flutter_hooks 这个写法使用的hook对于react和vue3开发者非常的友好,符合使用习惯,最重要的是很方便。

写法如下:

使用HookConsumerWidget,定义一个响应式更新到变量使用useState: final isButtonDisabled = useState<bool>(false);,这样就可以愉快的使用定义的变量了,页面会响应式更新的。

dart 复制代码
class EmailVerificationScreen extends HookConsumerWidget {
  final Map<String, dynamic>? arguments;

  const EmailVerificationScreen(this.arguments, {super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final formKey = useMemoized(() => GlobalKey<FormState>());
    final emailCodeController = useTextEditingController();

    final isButtonDisabled = useState<bool>(false);

    Future<void> nextStep(BuildContext context) async {
      try {
        SRouter(context).pushNamed("SetPassword", queryParameters: {
          "account": arguments?['account'],
        });
      } catch (e) {
        print(e);
      }
    }

    useEffect(() {
      listener() {
        isButtonDisabled.value = emailCodeController.text.length < 6;
      }

      emailCodeController.addListener(listener);
      return () {
        emailCodeController.removeListener(listener);
      };
    }, [emailCodeController]);

    return Form(
      key: formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            S.of(context).emailVerification,
            style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w400),
          ),
          SizedBox(height: 8.w),
          Text(
            S.current
                .enterThe6DigitVerificationCodeReceivedInYourEmailTheCodeIsValidFor30Minutes,
            style: TextStyle(
              color: const Color.fromRGBO(143, 143, 143, 1),
              fontSize: 12.sp,
              fontWeight: FontWeight.w400,
            ),
          ),
          SizedBox(height: 39.w),
          SStyledInput(
            controller: emailCodeController,
            labelText: S.of(context).emailVerificationCode,
            validator: (value) => Validators.validateEmail(value, context),
          ),
          SizedBox(height: 10.w),
          SizedBox(
            height: 50.w,
            width: double.infinity,
            child: ElevatedButton(
              onPressed:
                  isButtonDisabled.value ? null : () => nextStep(context),
              child: Text(S.of(context).nextStep),
            ),
          ),
          SizedBox(
            height: 26.w,
          ),
          GestureDetector(
            onTap: () {
              SRouter(context).pushNamed("Register");
            },
            child: Text(
              S.of(context).didNotReceiveTheCode,
              style: const TextStyle(
                color: Color.fromRGBO(79, 160, 255, 1.0),
                fontSize: 12,
              ),
            ),
          )
        ],
      ),
    );
  }
}
  • 全局状态管理:

大体如下,使用一个全局的NotifierProvider

scala 复制代码
final userProvider = NotifierProvider<UserProvide, UserState>(UserProvide.new);

class UserState {
  /// 当前活跃用户
  User? activeUser;

  /// 多用户会话管理
  List<User> userList = [];

  String? get getToken {
    return activeUser?.accessToken;
  }
}

class UserProvide extends Notifier<UserState> {
  @override
  UserState build() => UserState();

  /// 登录
  Future<void> login(User user) async {
    final existingUser =
        state.userList.firstWhereOrNull((u) => u.userId == user.userId);

getx

getx是使用的false.obs的形式

dart 复制代码
var isButtonDisabled = false.obs;

路由对比

由于getx还有路由的相关功能,所以这里再加一个getx 路由和go_router的对比,go_router是官方推荐的。

getx router

  • router.dart getx路由提供了一些转场动画,挺方便的
php 复制代码
const Transition transition = Transition.rightToLeft;

List<GetPage> routes = [
  GetPage(
      name: '/',
      page: () => const HomeScreen(username: ''),
      transition: transition),
  GetPage(
      name: '/login',
      page: () => const DefaultLayout(
            title: LocaleKeys.login,
            child: LoginScreen(),
          ),
      transition: transition),
  GetPage(
      name: '/welcomeBack',
      page: () => DefaultLayout(
            title: LocaleKeys.welcomeBack,
            child: WelcomeBackScreen(),
          ),
      transition: transition),
  GetPage(
      name: '/notFound',
      page: () => const NotFoundScreen(),
      transition: Transition.fade),
];
  • 跳转:

可以看到getx并没有依赖context

dart 复制代码
Get.toNamed(
  '/welcomeBack',
  arguments: {'email': email},
);

go_router

这里要吐槽一句go_router的官方文档容易让人误解。上来就让人context.go('/users/123'),也没说清楚一点,这个操作数据是不进入路由栈的。所以路由回退不了,让我疑惑半天,差点就换库了,和群友交流了一下才晓得,不少哥们都被坑过。

  • index.dart
php 复制代码
final goRouter = GoRouter(
  initialLocation: FlutterConfig.initialLocation,
  redirect: routeBeforeHook,
  // debugLogDiagnostics: true,
  routes: baseRoutes + whiteList,
);
  • routes.dart
php 复制代码
List<GoRoute> baseRoutes = [  GoRoute(
  name: "Home",
  path: '/',
  builder: (context, state) => const HomeScreen(),
),];
  • 跳转:
ini 复制代码
context.pushNamed(name);
  • 这里需要说一下的就是go_router配合riverpod的使用,如何在go_router里面拿到riverpod的状态:
javascript 复制代码
String? routeBeforeHook(BuildContext context, GoRouterState state) {
  final userState = ProviderScope.containerOf(context).read(userProvider);

  if (userState.getToken == null &&
      !whiteList.map((e) => e.path).contains(state.uri.path)) {
    return '/login';
  } else {
    return null;
  }
}

国际化对比

getx

如果使用getx的国际化功能,建议配合getx_cli使用,因为这个可以根据json文件生成,json是通用文件,可以方便替换和多项目使用,而且生成之后就有提示了。

dart 复制代码
import 'package:get/get.dart';

class Messages extends Translations {
  @override
  Map<String, Map<String, String>> get keys => {
        'zh_CN': {
          'hello': '你好 世界',
        },
        'de_DE': {
          'hello': 'Hallo Welt',
        }
      };
}
  • 使用:

这个使用方式虽然也方便,但是没有提示

bash 复制代码
Text('title'.tr);

flutter_localizations

这是官方推荐的,最好配合编辑器插件Flutter Intl使用。在lib目录下创建I10n文件夹,使用的是.arb后缀的文件,这个文件的格式和json一模一样,插件就会自动帮助生成相应代码了。

总结

总结下来,于我来说大概就是两条路线,一条是getx全家桶一把梭,一条就是hooks_riverpod + fluter_hooks + go_route + flutter_localizations。

接下来还要写react的项目,我对react的生态也不太熟,还要去继续去调研一下react的状态库以及相关的生态。

相关推荐
天蓝色的鱼鱼3 分钟前
别再瞎转Base64了!一文打通前端二进制任督二脉
前端
哟哟耶耶6 分钟前
Plugin-安装Vue.js devtools6.6.3扩展(组件层级可视化)
前端·javascript·vue.js
卖火箭的小男孩13 分钟前
Flutter 开发代码规范(优化完善版)
flutter·代码规范
梦65017 分钟前
【前端实战】图片元素精准定位:无论缩放,元素始终钉在指定位置
前端·html·css3
烟袅36 分钟前
一文搞懂 useRef:它到底在“存”什么?
前端·react.js
Knight_AL41 分钟前
Vue + Spring Boot 项目统一添加 `/wvp` 访问前缀实践
前端·vue.js·spring boot
前端er小芳1 小时前
前端虚拟列表滚动功能实现与核心知识点详解
前端
wuhen_n1 小时前
Promise状态机与状态流转
前端
3秒一个大1 小时前
React 中的 useMemo 与 useCallback:性能优化的利器
前端·react.js
cj81401 小时前
Node.js基本概念理解
前端·node.js