flutter 如何根据所选的GoRouter路线更改应用程序栏标题?

63lcw9qa  于 2023-01-06  发布在  Flutter
关注(0)|答案(2)|浏览(340)

我想使用固定的ScaffoldAppBar实现基于GoRouter的导航,但要根据所选路线动态更改AppBar的标题。
我使用GoRouter的ShellRoute来修复ScaffoldAppBar,并尝试使用riverpod Provider更改标题:

final titleProvider = StateProvider((ref) => 'Title');

ShellRoute(
   builder: (BuildContext context, GoRouterState state, Widget child) {
       return Scaffold(
         body: child,
         appBar: CustomAppBar()
       );
   },
   routes: [
       GoRoute(
          path: DashboardScreenWeb.routeLocation,
          name: DashboardScreenWeb.routeName,
          builder: (context, state) {
             ref.read(titleProvider.state).state = DashboardScreenWeb.title;
             return const DashboardScreenWeb();
          },
       ),
       GoRoute(
          path: BusinessDataScreen.routeLocation,
          name: BusinessDataScreen.routeName,
          builder: (context, state) {
            ref.read(titleProvider.state).state = BusinessDataScreen.title;
            return const BusinessDataScreen();
          },
        ),
....

我的CustomAppBar widget使用此提供程序的方式如下:

class CustomAppBar extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var title = ref.watch(titleProvider);
    return new AppBar(
      title: Text(title!)
    );
  }
}

但是,我遇到了很多异常,很可能是因为我在错误的时间更改了提供程序的状态。对此我该怎么办?

======== Exception caught by widgets library =======================================================
The following StateNotifierListenerError was thrown building Builder(dirty):
At least listener of the StateNotifier Instance of 'StateController<String>' threw an exception
when the notifier tried to update its state.

The exceptions thrown are:

setState() or markNeedsBuild() called during build.
This UncontrolledProviderScope widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
  UncontrolledProviderScope
The widget which was currently being built when the offending call was made was:
aamkag61

aamkag611#

使用状态属性state.location并将title传递给AppBar

Go工艺路线
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorKey = GlobalKey<NavigatorState>();

final router = GoRouter(
  initialLocation: '/',
  navigatorKey: _rootNavigatorKey,
  routes: [
    ShellRoute(
      navigatorKey: _shellNavigatorKey,
      pageBuilder: (context, state, child) {
        String title;
        switch (state.location) {              // 👈 Using state.location to set title
          case '/':
            title = "Initial Screen";
            break;
          case '/home':
            title = "Home Screen";
            break;
          default:
            title = "Default Screen";
        }
        return NoTransitionPage(
            child: ScaffoldAppAndBottomBar(
          appTitle: title,                    // 👈 pass title here
          child: child,
        ));
      },
      routes: [
        GoRoute(
          parentNavigatorKey: _shellNavigatorKey,
          path: '/home',
          name: 'Home Title',
          pageBuilder: (context, state) {
            return const NoTransitionPage(
              child: Scaffold(
                body: Center(
                  child: Text("Home"),
                ),
              ),
            );
          },
        ),
        GoRoute(
          path: '/',
          name: 'App Title',
          parentNavigatorKey: _shellNavigatorKey,
          pageBuilder: (context, state) {
            return const NoTransitionPage(
              child: Scaffold(
                body: Center(child: Text("Initial")),
              ),
            );
          },
        ),
      ],
    ),
  ],
);
自定义应用程序栏
class CustomAppBar extends StatelessWidget {
  Widget child;
  String? appTitle;
  CustomAppBar(
      {super.key, required this.child, required this.appTitle});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text(appTitle ?? "Default"),
      ),
      body: SafeArea(child: child),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.go('/home');
        },
        child: const Icon(Icons.home),
      ),
    );
  }
}

输出:

ilmyapht

ilmyapht2#

你应该像这样定义你的titleProvider:

final titleProvider = Provider<String>((ref) => 'Title');

并更新提供程序:

GoRoute(
      path: BusinessDataScreen.routeLocation,
      name: BusinessDataScreen.routeName,
      builder: (context, state) {

        return ProviderScope(
    overrides: [
        titleProvider.overrideWithValue('youTitleHere')
       ]
      child: const BusinessDataScreen(),
        );
      },
    ),

相关问题