CupertinoTabBar 和 BottomNavigationBarItem UI 未使用 ChangeNotifierProvider 更新

发布于 2025-01-17 04:26:58 字数 3820 浏览 2 评论 0原文

我正在使用 Flutter Provider 包来管理 currentIndex CupertionTabBar 的状态。我这样做而不是使用 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 并未更新。

感谢您的帮助。

I'm using Flutter Provider package for managing currentIndex state of CupertionTabBar. I'm doing this instead of using setState of StatefulWidget because I want to programmatically update current active tab from within the tab view pages.

Here's the app code:

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();
  }
}

Checkout the live code with results on DartPad.

As you can see although AppState.currentTabIndex is updating and reflected in Text widget but current active BottomNavigationBarItem UI is not updating.

Your help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

小傻瓜 2025-01-24 04:26:58

正如 CupertinoTabScaffold文档 中所述tabBar 属性:

CupertinoTabBar.currentIndex 仅用于初始化
CupertinoTabController(当未提供控制器时)。随后
提供不同的 CupertinoTabBar.currentIndex 不会影响
脚手架或选项卡栏的活动选项卡索引。以编程方式更改
活动选项卡索引,使用 CupertinoTabController。

如果提供了 CupertinoTabBar.onTap,它仍然会被调用。
CupertinoTabScaffold 也自动监听
CupertinoTabBar的onTap更改控制器的索引并更改
CupertinoTabScaffold 自己的主要内容中主动显示的选项卡
面积。

因此,在 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();
  }
}

As mentioned in the docs of CupertinoTabScaffold tabBar property:

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.

So, holding instance of CupertinoTabController in AppState and updating index on it works:

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();
  }
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文