CupertinoTabBar 和 BottomNavigationBarItem UI 未使用 ChangeNotifierProvider 更新

CupertinoTabBar and BottomNavigationBarItem UI not updating with ChangeNotifierProvider

我正在使用 Flutter Provider 包来管理 CupertionTabBarcurrentIndex 状态。我这样做而不是使用 StatefulWidgetsetState 因为我想以编程方式从选项卡视图页面中更新当前活动选项卡。

这是应用代码:

import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      theme: const CupertinoThemeData(
        brightness: Brightness.light,
      ),
      home: ChangeNotifierProvider(
        create: (context) => AppState(),
        child: const TabsPage(),
      ),
    );
  }
}

class TabsPage extends StatelessWidget {
  const TabsPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<AppState>(
      builder: (context, appState, child) {
        return CupertinoTabScaffold(
          tabBar: CupertinoTabBar(
            backgroundColor: CupertinoColors.white,
            currentIndex: appState.currentTabIndex,
            onTap: (index) => appState.currentTabIndex = index,
            items: const [
              BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.house),
                activeIcon: Icon(CupertinoIcons.house_fill),
              ),
              BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.play_rectangle),
                activeIcon: Icon(CupertinoIcons.play_rectangle_fill),
              ),
              BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.heart),
                activeIcon: Icon(CupertinoIcons.heart_fill),
              ),
              BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.music_albums),
                activeIcon: Icon(CupertinoIcons.music_albums_fill),
              ),
              BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.ellipsis_circle),
                activeIcon: Icon(CupertinoIcons.ellipsis_circle_fill),
              ),
            ],
          ),
          tabBuilder: (context, index) {
            return const TabPage();
          },
        );
      },
    );
  }
}

class TabPage extends StatelessWidget {
  const TabPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CupertinoTabView(
      builder: (context) => Consumer<AppState>(
        builder: (context, appState, child) => Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Current Tab: ${appState.currentTabIndex}'),
              const SizedBox(height: 8.0),
              ...List.generate(
                5,
                    (index) => Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: CupertinoButton.filled(
                    child: Text('Go to tab $index'),
                    onPressed: () => appState.currentTabIndex = index,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class AppState extends ChangeNotifier {
  int _currentTabIndex = 0;

  int get currentTabIndex => _currentTabIndex;

  set currentTabIndex(int index) {
    _currentTabIndex = index;
    notifyListeners();
  }
}

检查 DartPad 上的实时代码和结果。

如您所见,虽然 AppState.currentTabIndex 正在更新并反映在 Text 小部件中,但当前活动的 BottomNavigationBarItem UI 没有更新。

感谢您的帮助。

CupertinoTabScaffolddocs中所述tabBar属性:

The CupertinoTabBar.currentIndex is only used to initialize a CupertinoTabController when no controller is provided. Subsequently providing a different CupertinoTabBar.currentIndex does not affect the scaffold or the tab bar's active tab index. To programmatically change the active tab index, use a CupertinoTabController.

If CupertinoTabBar.onTap is provided, it will still be called. CupertinoTabScaffold automatically also listen to the CupertinoTabBar's onTap to change the controller's index and change the actively displayed tab in CupertinoTabScaffold's own main content area.

因此,在 AppState 中保存 CupertinoTabController 的实例并在其上更新 index 有效:

import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      theme: const CupertinoThemeData(
        brightness: Brightness.light,
      ),
      home: ChangeNotifierProvider(
        create: (context) => AppState(),
        child: const TabsPage(),
      ),
    );
  }
}

class TabsPage extends StatelessWidget {
  const TabsPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final appState = Provider.of<AppState>(context, listen: false);
    return CupertinoTabScaffold(
      controller: appState.tabController,
      tabBar: CupertinoTabBar(
        backgroundColor: CupertinoColors.white,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.house),
            activeIcon: Icon(CupertinoIcons.house_fill),
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.play_rectangle),
            activeIcon: Icon(CupertinoIcons.play_rectangle_fill),
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.heart),
            activeIcon: Icon(CupertinoIcons.heart_fill),
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.music_albums),
            activeIcon: Icon(CupertinoIcons.music_albums_fill),
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.ellipsis_circle),
            activeIcon: Icon(CupertinoIcons.ellipsis_circle_fill),
          ),
        ],
      ),
      tabBuilder: (context, index) {
        return const TabPage();
      },
    );
  }
}

class TabPage extends StatelessWidget {
  const TabPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CupertinoTabView(
      builder: (context) {
        final appState = Provider.of<AppState>(context, listen: false);
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Current Tab: ${appState.tabController.index}'),
              const SizedBox(height: 8.0),
              ...List.generate(
                5,
                (index) => Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: CupertinoButton.filled(
                    child: Text('Go to tab $index'),
                    onPressed: () => appState.tabController.index = index,
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

class AppState extends ChangeNotifier {
  final tabController = CupertinoTabController();

  @override
  void dispose() {
    tabController.dispose();
    super.dispose();
  }
}