Обновить счетчик значков корзины в AppBar из нескольких виджетов

Я использую многоразовый виджет 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 для управления состоянием, но в моем случае это не работает, поскольку виджет панели приложений не имеет состояния.


person Mohammad    schedule 06.04.2021    source источник
comment
Могу я спросить, пользуетесь ли вы услугами каких-либо провайдеров?   -  person Fahmi Sawalha    schedule 06.04.2021
comment
Нет, не использую провайдера   -  person Mohammad    schedule 06.04.2021


Ответы (3)


Я предлагаю вам использовать любые поставщики, такие как provider пакет или riverpod, вам нужно что-то под названием notifyListeners, чтобы уведомлять значок корзины каждый раз, когда вы добавляете новый товар в корзину, иначе товар в вашей корзине не будет обновляться кроме перестройки виджета; нравится навигация и тому подобное.

вы можете проверить riverpod и provider на pub.dev, убедитесь, что вы полностью понимаете документацию, потому что это может быть сложно!

Надеюсь, этот ответ вам поможет!

person Fahmi Sawalha    schedule 06.04.2021
comment
Я попробую использовать провайдера и проверю, может ли он решить проблему - person Mohammad; 06.04.2021
comment
Провайдер решил мою проблему. Я использовал этот учебник, и теперь он работает. flutter.dev/docs/development/data-and-backend/ state-mgmt / simple - person Mohammad; 06.04.2021

вы хотите управлять состоянием приложения. То, как ты делаешь, довольно сложно.

Может быть, вам стоит поискать какое-нибудь решение для управления государством.

вот ссылка на хорошее вступление, в котором рассказывается о том, чего вы пытаетесь достичь введение в управление государством

Вот список некоторых популярных госуправлений:

  • Provider (прост в использовании и рекомендован сообществом Flutter)
  • Bloc (подходит для очень больших проектов)
  • GetX (простой и удобный в использовании)
  • Riverpod (это провайдер, но более мощный)

Не существует идеального выбора, просто используйте то, что вы сочли хорошим, по мере необходимости.

person Boris Kamtou    schedule 06.04.2021
comment
Спасибо, Борис. Я использовал провайдера, чтобы справиться с этим. - person Mohammad; 06.04.2021
comment
Хорошо. Хороший выбор. - person Boris Kamtou; 06.04.2021

Как предложили Фахми Савальха и Борис Камту, я использовал пакет провайдера для решения проблемы.

Я поделюсь кодом, если он кому-то понадобится. Код помогает вам создать настраиваемый AppBar как виджет без сохранения состояния с такими параметрами, как title, context, showBackButton, showActions.

А также код помогает вам использовать пакет провайдера для управления состоянием и обновления пользовательского интерфейса с разных экранов.

Сначала создайте класс CartCounterNotifier в любом доступном месте вашего приложения. Он должен расширять ChangeNotifier, чтобы уведомлять слушателей при изменении значения, чтобы пользовательский интерфейс, использующий этого поставщика, перестраивал:

CartManager - это миксин, в котором я управляю данными корзины в БД. Функция 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 (), который будет вызываться при изменении значения.

Я создал настраиваемую панель приложений для использования на всех страницах. Это виджет без состояния, который реализует PreferSizeWidget.

Оберните виджет в том месте, где вы хотите показывать счетчик, с помощью 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);
  }
}

Затем оберните свое приложение main.dart с помощью ChangeNotifierProvider

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();
person Mohammad    schedule 07.04.2021