从多个小部件更新 AppBar 中的购物车徽章计数器
Update Cart badge counter in AppBar from multiple widgets
我正在使用具有标题和操作按钮的可重用 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);
}
}
应用栏用于所有屏幕,但参数不同。
我想在用户添加到购物车、更新购物车、从购物车中删除时更新应用栏中的计数器。这可能发生在多个页面上:
商品列表页、商品详情页、购物车页、搜索建议小部件。所有这些页面都有购物车操作(add/update/delete)。
我不想在每个页面中管理这个计数器。我需要一种方法来在一个地方管理它,并在购物车更新时收到通知以更新徽章
我搜索了很多。有些使用 GLobalKey 来管理状态,但在我的情况下不起作用,因为应用栏小部件是无状态的,不能是有状态的。
我建议您使用任何类型的提供商,例如 provider
包或 riverpod
,您需要一个名为 notifyListeners
的东西,以便在您每次添加新商品时通知购物车徽章项目到您的购物车,否则您的购物车项目将不会更新,除非在小部件重建时;比如导航等等。
您可以从 pub.dev 检查 riverpod 和提供程序,确保完全理解文档,因为它可能很棘手!
希望这个回答对您有所帮助!
您想做的是管理应用程序的状态。你的做法很辛苦。
也许您应该看看一些状态管理解决方案。
这里有一个 link 很好的介绍,它讲述了你想要实现的目标
intro to state managment
以下是一些流行的状态管理列表:
世上没有十全十美的选择,适合自己的就好。
根据 Fahmi Sawalha 和 Boris Kamtou 的建议,我使用了 provider 包来解决问题。
我会分享代码以备不时之需。该代码可帮助您将自定义 AppBar 创建为具有标题、上下文、showBackButton、showActions 等参数的无状态小部件。
此外,该代码还可以帮助您使用提供程序包管理状态并从不同的屏幕更新 ui。
首先在您应用程序的任何可访问位置创建 CartCounterNotifier class。它应该扩展 ChangeNotifier 以便在值更改时通知侦听器,以便 ui 使用此提供程序 rebuilds:
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();
}
}
class有两种方法:
初始化计数器的构造函数和值更改时调用的 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();
我正在使用具有标题和操作按钮的可重用 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);
}
}
应用栏用于所有屏幕,但参数不同。
我想在用户添加到购物车、更新购物车、从购物车中删除时更新应用栏中的计数器。这可能发生在多个页面上: 商品列表页、商品详情页、购物车页、搜索建议小部件。所有这些页面都有购物车操作(add/update/delete)。
我不想在每个页面中管理这个计数器。我需要一种方法来在一个地方管理它,并在购物车更新时收到通知以更新徽章
我搜索了很多。有些使用 GLobalKey 来管理状态,但在我的情况下不起作用,因为应用栏小部件是无状态的,不能是有状态的。
我建议您使用任何类型的提供商,例如 provider
包或 riverpod
,您需要一个名为 notifyListeners
的东西,以便在您每次添加新商品时通知购物车徽章项目到您的购物车,否则您的购物车项目将不会更新,除非在小部件重建时;比如导航等等。
您可以从 pub.dev 检查 riverpod 和提供程序,确保完全理解文档,因为它可能很棘手!
希望这个回答对您有所帮助!
您想做的是管理应用程序的状态。你的做法很辛苦。
也许您应该看看一些状态管理解决方案。
这里有一个 link 很好的介绍,它讲述了你想要实现的目标 intro to state managment
以下是一些流行的状态管理列表:
世上没有十全十美的选择,适合自己的就好。
根据 Fahmi Sawalha 和 Boris Kamtou 的建议,我使用了 provider 包来解决问题。
我会分享代码以备不时之需。该代码可帮助您将自定义 AppBar 创建为具有标题、上下文、showBackButton、showActions 等参数的无状态小部件。
此外,该代码还可以帮助您使用提供程序包管理状态并从不同的屏幕更新 ui。
首先在您应用程序的任何可访问位置创建 CartCounterNotifier class。它应该扩展 ChangeNotifier 以便在值更改时通知侦听器,以便 ui 使用此提供程序 rebuilds:
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();
}
}
class有两种方法:
初始化计数器的构造函数和值更改时调用的 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();