Flutter路由管理或导航插件go_provider的使用

Flutter路由管理或导航插件go_provider的使用

GoProvider简介

GoProvider旨在简化go_router路由内的provider作用域,提供以下特性:

  • 🎯 作用域简单性:特定于路由的状态访问,使代码更简洁。
  • 🏗️ 模块化设计:不再需要在应用顶部放置1000个providers。
  • ✨ 自动生命周期:providers在进入路由时初始化,并在路由弹出时自动销毁。
  • 🚀 流畅的状态管理:与providerflutter_bloc轻松集成。
  • 🚌 总线防护:一个易于维护的单文件库,必要时可以复制粘贴。

The Problem(问题)

在未使用GoProvider之前,状态管理存在一些问题。例如,无法在某些页面中访问到UserState

routes: [
  GoRoute(
    path: '/',
    builder: (context, state) => LoginPage(), // ❌ can't access UserState
  ),
  GoRoute(
    path: '/home',
    builder: (context, state) => ChangeNotifierProvider( // or BlocProvider
      create: (_) => UserState(),
      child: const HomePage(), // ✅ can access UserState
    ),
    routes: [
      GoRoute(
        path: 'details',
        builder: (context, state) => const DetailsPage(), // ❌ throws ProviderNotFoundException
      ),
    ],
  ),
]

GoProvider Solution(解决方案)

通过使用GoProviderRoute,可以在指定的路由上提供状态,从而确保所有子路由都可以访问这些状态。

routes: [
  GoRoute(
    path: '/',
    builder: (context, state) => LoginPage(), // ❌ can't access UserState
  ),
  GoProviderRoute(
    path: '/home',
    providers: [
      ChangeNotifierProvider(create: (_) => UserState()), // or BlocProvider
    ],
    builder: (context, state) => const HomePage(), // ✅ can access UserState
    routes: [
      GoRoute(
        path: 'details',
        builder: (context, state) => const DetailsPage(), // ✅ can access UserState too!
      ),
    ],
  ),
]

使用ShellProviderRoute

你还可以使用ShellProviderRoute来为多个子路由提供相同的状态。

routes: [
  ShellProviderRoute(
    providers: [
      ChangeNotifierProvider(create: (_) => FooState()), // or BlocProvider
    ],
    builder: (context, state, child) => ShellPage(child: child), // ✅ can access FooState
    routes: [
      GoRoute(
        path: '/a',
        builder: (context, state) => const PageA(), // ✅ can access FooState
      ),
      GoRoute(
        path: '/b',
        builder: (context, state) => const PageB(), // ✅ can access FooState
      ),
    ],
  ),
]

Issues(问题)

由于GoProviderRoute有自己的NavigatorcanPop方法总是返回false。这意味着隐式后退/关闭按钮不会显示。这是一个已知的问题,当使用ShellRoute路由时尤为明显。

为了解决GoRouter的隐式弹出问题,你可以使用GoPopButton

GoPopButton(), // CloseButton/BackButton that pops the current route (like AppBar's leading)

也建议在GoProviderRoute路由内部使用context.pop而不是Navigator.pop

Contribution(贡献)

欢迎提交拉取请求或在我们的GitHub仓库中打开问题。如果你喜欢这个项目,请不要忘记点赞/收藏。

示例代码

以下是一个完整的示例代码,展示了如何使用go_provider进行路由管理和状态传递。

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_provider/go_provider.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(MaterialApp.router(routerConfig: goRouter));
}

class BlocCubit extends Cubit<String> {
  BlocCubit() : super('Hello from BlocCubit! 🚀');
}

final goRouter = GoRouter(
  routes: [
    GoProviderRoute(
      path: '/',
      providers: [
        BlocProvider(create: (_) => BlocCubit()),
      ],
      builder: (context, state) => const PageA(), // ✅ can access BlocCubit
      routes: [
        GoRoute(
          path: 'b',
          builder: (context, state) => const PageB(), // ✅ can access BlocCubit
        ),
      ],
    ),
    GoRoute(
      path: '/c',
      builder: (context, state) => const PageC(), // ❌ can't access BlocCubit
    ),
  ],
);

class PageA extends StatelessWidget {
  const PageA({super.key});

  @override
  Widget build(BuildContext context) {
    final bloc = context.watch<BlocCubit>();

    return Scaffold(
      appBar: AppBar(title: Text('Page A - ${bloc.state}')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
              onPressed: () => context.go('/b'),
              child: const Text('Go to Page B'),
            ),
            ElevatedButton(
              onPressed: () => context.go('/c'),
              child: const Text('Go to Page C'),
            ),
          ],
        ),
      ),
    );
  }
}

class PageB extends StatelessWidget {
  const PageB({super.key});

  @override
  Widget build(BuildContext context) {
    final blocA = context.watch<BlocCubit>();

    return Scaffold(
      appBar: AppBar(title: Text('Page B - ${blocA.state}')),
    );
  }
}

class PageC extends StatelessWidget {
  const PageC({super.key});

  @override
  Widget build(BuildContext context) {
    final blocA = context.watch<BlocCubit?>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Page C - ${blocA?.state ?? 'Bloc not found 😔'}'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.go('/'),
          child: const Text('Go to Page A'),
        ),
      ),
    );
  }
}

这个示例展示了如何使用go_provider进行路由管理和状态传递,确保在不同的页面之间可以正确地访问和管理状态。


更多关于Flutter路由管理或导航插件go_provider的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter路由管理或导航插件go_provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,go_provider 是一个用于管理路由和导航的插件,它提供了一种声明式的方式来处理应用中的页面跳转。下面是一个简单的示例,展示了如何在Flutter应用中使用 go_provider 进行路由管理。

首先,确保你已经在 pubspec.yaml 文件中添加了 go_router 依赖(注意:go_provider 并非一个广泛认知的官方或社区库名称,这里我们假设你指的是 go_router,它是Flutter官方推荐的路由管理库之一,提供了类似的功能)。如果实际上你指的是另一个具体的库,请确保替换为正确的依赖名称。

dependencies:
  flutter:
    sdk: flutter
  go_router: ^5.0.0  # 请检查最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,我们来看一个如何使用 go_router 进行路由管理的示例:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final router = GoRouter(
      routes: [
        GoRoute(
          path: '/',
          builder: (context, state) => HomeScreen(),
          routes: [
            GoRoute(
              path: 'details/:id',
              builder: (context, state) {
                final id = state.params['id']!;
                return DetailsScreen(id: id);
              },
            ),
          ],
        ),
      ],
    );

    return MaterialApp.router(
      router: router,
      title: 'GoRouter Demo',
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final GoRouter router = GoRouter.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            router.go('/details/123');
          },
          child: Text('Go to Details'),
        ),
      ),
    );
  }
}

class DetailsScreen extends StatelessWidget {
  final String id;

  DetailsScreen({required this.id});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Details Screen'),
      ),
      body: Center(
        child: Text('Details for ID: $id'),
      ),
    );
  }
}

在这个示例中:

  1. 我们定义了一个 GoRouter 实例,其中包含了应用的路由配置。
  2. 根路由('/')指向 HomeScreen,并且有一个子路由('/details/:id'),它接受一个路径参数 id
  3. HomeScreen 包含一个按钮,当用户点击该按钮时,会导航到 DetailsScreen,并将 id 参数设置为 123
  4. DetailsScreen 显示传入的 id 参数。

这种方式使得路由管理变得清晰且易于维护,同时利用了Flutter的声明式编程风格。如果你实际上是在寻找一个名为 go_provider 的特定库,并且它不是 go_router,请确保查阅该库的官方文档以获取正确的使用方法和示例代码。

回到顶部