【问题标题】:Update Cart badge counter in AppBar from multiple widgets从多个小部件更新 AppBar 中的购物车徽章计数器
【发布时间】:2021-07-02 04:15:21
【问题描述】:

我正在使用具有标题和操作按钮的可重用 AppBar 小部件。

在应用栏操作中,有收藏图标按钮和带有徽章的购物车图标按钮,显示购物车中的商品总数:

应用栏小部件:

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

class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
  final BuildContext context;
  final String title;
  final bool showBackButton;
  final Widget widget;
  final bool showActions;

  CustomAppBar({
    @required this.context,
    @required this.title,
    this.showBackButton = true,
    this.widget,
    this.showActions = true,
  });

  @override
  Widget build(BuildContext context) {
    return AppBar(
      title: Text(title),
      leading: showBackButton
          ? new IconButton(
              icon: new Icon(
                Icons.arrow_back,
              ),
              onPressed: () {
                if (widget != null) {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => widget,
                    ),
                  );
                  return true;
                } else {
                  Navigator.pop(context);
                }
              },
            )
          : null,
      actions: !showActions ? null : <Widget>[
        IconButton(
          icon: const Icon(Icons.favorite_border),
          onPressed: () {
            Navigator.pushReplacement(
              context,
              MaterialPageRoute<void>(
                builder: (BuildContext context) {
                  return MainHome(
                    selectedIndex: 1,
                  );
                },
              ),
            );
          },
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 5),
          child: Badge(
            position: BadgePosition.topEnd(top: 3, end: 3),
            animationDuration: Duration(milliseconds: 300),
            animationType: BadgeAnimationType.slide,
            badgeColor: Colors.white,
            toAnimate: true,
            badgeContent: Text(
              '5',
              style: TextStyle(
                  fontSize: 8,
                  color: Theme.of(context).primaryColor,
                  fontWeight: FontWeight.bold),
            ),
            child: IconButton(
              icon: const Icon(Icons.shopping_cart_rounded),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute<void>(
                    builder: (BuildContext context) {
                      return MainHome(
                        selectedIndex: 2,
                      );
                    },
                  ),
                );
              },
            ),
          ),
        ),
      ],
    );
  }

  @override
  Size get preferredSize {
    return new Size.fromHeight(kToolbarHeight);
  }
}

应用栏用于所有屏幕,但参数不同。

每当用户添加到购物车、更新购物车、从购物车中删除时,我都想更新应用栏中的计数器。这可能发生在多个页面上: 产品列表页面、产品详情页面、购物车页面、搜索建议小部件。所有这些页面都有购物车操作(添加/更新/删除)。

我不想在每个页面中管理这个计数器。我需要一种方法在一个地方管理它,并在购物车更新时收到通知以更新徽章

我搜索了很多。有些使用 GLobalKey 来管理状态,但在我的情况下不起作用,因为 appbar 小部件是无状态的,不能是有状态的。

【问题讨论】:

  • 请问您是否使用任何类型的提供商?
  • 不使用提供者

标签: flutter flutter-appbar


【解决方案1】:

我建议您使用任何类型的提供商,例如 provider 包或 riverpod ,您需要一个名为 notifyListeners 的东西,以便在每次将新商品添加到购物车时通知购物车徽章,否则您将购物车除了小部件重建外,您的项目不会更新;比如导航之类的。

您可以从 pub.dev 查看 riverpod 和 provider,确保完全理解文档,因为这可能很棘手!

希望这个答案对你有帮助!

【讨论】:

【解决方案2】:

您要做的是管理应用程序的状态。你的做法很辛苦。

也许你应该看看一些状态管理解决方案。

这里有一个很好的介绍链接,它讨论了你想要达到的目标 intro to state managment

以下是一些流行的状态管理列表:

  • Provider(易于使用,由 Flutter 社区推荐)
  • Bloc(适合大型项目)
  • GetX(简单易用)
  • Riverpod(它是提供者,但更强大)

没有完美的选择,只需使用您认为适合需要的东西。

【讨论】:

  • 谢谢鲍里斯。我使用提供者来处理它。
【解决方案3】:

按照 Fahmi Sawalha 和 Boris Kamtou 的建议,我使用提供程序包来解决问题。

如果有人需要,我会分享代码。该代码可帮助您将自定义 AppBar 创建为具有标题、上下文、showBackButton、showActions 等参数的无状态小部件。

而且代码还可以帮助您使用提供程序包来管理状态并从不同的屏幕更新用户界面。

首先在应用中任何可访问的位置创建 CartCounterNotifier 类。它应该扩展 ChangeNotifier 以便在值更改时通知侦听器,以便使用此提供程序的 UI 重建:

CartManager 是一个 Mixin,我在数据库中管理购物车数据。 getCartItemsCount() 函数获取购物车中商品数量的总和。

cart_counter.dart

import 'package:flutter/material.dart';

class CartCounterNotifier extends ChangeNotifier with CartManager {
  int _value = 0;

  int get value => _value;

  CartCounterNotifier() {
    getCartItemsCount().then((counter) {
      _value = counter ?? 0;
      notifyListeners();
    });
  }

  // You can send send parameters to update method. No need in my case. 
  //  Example:  void update(int newvalue) async { ...

  void update() async {
    int counter = await getCartItemsCount();

    _value = counter ?? 0;
    notifyListeners();
  }
}

该类有两个方法:

初始化计数器的构造函数和值改变时调用的update()方法。

我创建了自定义应用栏以在所有页面中使用。它是一个实现 PreferedSizeWidget 的无状态小部件。

使用 Consumer 将小部件包裹在您想要显示计数器的位置。最好尽可能深入。在我的例子中,我没有用消费者包装整个 AppBar,而是包装了显示计数器的文本小部件

custom_app_bar.dart

import 'package:badges/badges.dart'; // add badges to pubspec in order to add badge for icons
import 'package:flutter/material.dart';
import 'package:order_flutter_package/services/cart_counter.dart';
import 'package:order_flutter_package/ui/views/home_view/home_view.dart';
import 'package:provider/provider.dart';

class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
  final BuildContext context;
  final String title;
  final bool showBackButton;
  final Widget widget;
  final bool showActions;

  CustomAppBar({
    @required this.context,
    @required this.title,
    this.showBackButton = true,
    this.widget,
    this.showActions = true,
  });

  @override
  Widget build(BuildContext context) {
    return PreferredSize(
      preferredSize: Size.fromHeight(50),
      child: AppBar(
        title: Text(title),
        leading: showBackButton
            ? new IconButton(
                icon: new Icon(
                  Icons.arrow_back,
                ),
                onPressed: () {
                  if (widget != null) {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => widget,
                      ),
                    );
                    return true;
                  } else {
                    Navigator.pop(context);
                  }
                },
              )
            : null,
        actions: !showActions
            ? null
            : <Widget>[
                IconButton(
                  icon: const Icon(Icons.favorite_border),
                  onPressed: () {
                    Navigator.pushReplacement(
                      context,
                      MaterialPageRoute<void>(
                        builder: (BuildContext context) {
                          return MainHome(
                            selectedIndex: 1,
                          );
                        },
                      ),
                    );
                  },
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 5),
                  child: Badge(
                    position: BadgePosition.topEnd(top: 3, end: 3),
                    animationDuration: Duration(milliseconds: 300),
                    animationType: BadgeAnimationType.slide,
                    badgeColor: Colors.white,
                    toAnimate: true,
                    badgeContent: Consumer<CartCounterNotifier>(
                        builder: (context, cartCounter, child) {
                      return Text(
                        cartCounter.value.toString(),
                        style: TextStyle(
                            fontSize: 8,
                            color: Theme.of(context).primaryColor,
                            fontWeight: FontWeight.bold),
                      );
                    }),
                    child: IconButton(
                      icon: const Icon(Icons.shopping_cart_rounded),
                      onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute<void>(
                            builder: (BuildContext context) {
                              return MainHome(
                                selectedIndex: 2,
                              );
                            },
                          ),
                        );
                      },
                    ),
                  ),
                ),
              ],
      ),
    );
  }

  @override
  Size get preferredSize {
    return new Size.fromHeight(kToolbarHeight);
  }
}

然后用 ChangeNotifierProvider 包装你的 main.dart 应用

import 'package:provider/provider.dart';
import 'package:order_flutter_package/services/cart_counter.dart';

ChangeNotifierProvider(
        create: (context) => CartCounterNotifier(),
        child: MyApp(),
)

最后,为了更新计数器,在任何屏幕上,

// Very important to import provider else you got an error
import 'package:provider/provider.dart'; 

// On button pressed or any event
var cartCounter = context.read<CartCounterNotifier>();
cartCounter.update();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    • 2021-11-03
    • 2015-07-02
    相关资源
    最近更新 更多