【发布时间】:2021-07-04 17:49:22
【问题描述】:
我在使用 bloc 时遇到问题。集团状态正在更新,但似乎对 UI 没有影响。我已经使用示例应用程序重现了该问题。问题是从列表页面中删除一个项目不会改变颜色回到取消选择状态。实际上会发生什么,当您在主页上选择一个项目时,购物车按钮会出现,而在没有选择项目的状态下它会消失。因此,当您在主页上选择一个项目时,会出现购物车按钮。当您单击按钮时,您将导航到另一个页面,即 ListPage ,您可以在其中看到所有选定项目的列表以及与每个项目对应的删除按钮。当您删除一个项目时,它会从 ListPage 中删除,但它的颜色仍然是红色,即在 HomePage 上被选中。我必须再次单击以更改颜色,尽管它已被取消选择,即(item.active = false)从 ListPage 中删除它。只是颜色保持不变,它没有得到更新。代码文件如下:
item_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/item_model.dart';
import 'package:demo/repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
part 'items_event.dart';
part 'items_state.dart';
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
final ItemRepository itemRepository;
final CartBloc cartBloc;
ItemsBloc({
@required this.itemRepository,
@required this.cartBloc,
}) : super(ItemsLoading());
@override
Stream<ItemsState> mapEventToState(
ItemsEvent event,
) async* {
if (event is GetItems) {
yield* _mapGetItemsToState(event);
} else if (event is UpdateItem) {
yield* _mapUpdateItemToState(event);
}
}
Stream<ItemsState> _mapGetItemsToState(GetItems event) async* {
yield ItemsLoading();
try {
final _items = await itemRepository.loadItems();
yield ItemsLoaded(items: _items);
print('Yielded');
} catch (_) {
yield ItemsFailure();
}
}
Stream<ItemsState> _mapUpdateItemToState(UpdateItem event) async* {
try {
_updateRepositoryItems(event.item);
if (event.item.active) {
cartBloc.add(ShowItems());
} else {
cartBloc.add(RemoveItem(itemId: event.item.id));
}
yield* _mapGetItemsToState(GetItems());
} catch (_) {
yield ItemsFailure();
}
}
Item _updateRepositoryItems(Item item) => itemRepository.setItem = item;
}
cart_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:demo/item_data_model.dart';
import 'package:demo/repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
part 'cart_event.dart';
part 'cart_state.dart';
class CartBloc extends Bloc<CartEvent, CartState> {
final ItemRepository itemRepository;
CartBloc({@required this.itemRepository}) : super(CartLoading());
@override
Stream<CartState> mapEventToState(
CartEvent event,
) async* {
if (event is ShowItems) {
yield* _mapShowItemsToState(event);
} else if (event is RemoveItem) {
yield* _mapRemoveItemToState(event);
}
}
Stream<CartState> _mapShowItemsToState(ShowItems event) async* {
try {
final _selectedItems = await itemRepository.getSelectedItems;
if (_selectedItems.isNotEmpty) {
final data = ItemData(items: _selectedItems);
yield CartLoaded(itemData: data);
} else {
yield CartLoading();
}
} catch (_) {
yield CartFailure();
}
}
Stream<CartState> _mapRemoveItemToState(RemoveItem event) async* {
try {
final _selectedSounds = await itemRepository.getSelectedItems;
if (_selectedSounds.isEmpty) {
yield CartLoading();
} else {
yield* _mapShowItemsToState(ShowItems());
}
} catch (_) {
yield CartFailure();
}
}
}
主页.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/item_tile.dart';
import 'package:demo/list_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Home Page"),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: BlocBuilder<ItemsBloc, ItemsState>(
// ignore: missing_return
builder: (context, state) {
if (state is ItemsLoading) {
return Center(
child: CircularProgressIndicator(),
);
} else if (state is ItemsLoaded) {
print('Items Loaded');
return GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
childAspectRatio: 0.6,
children: [
for (var item in state.items) ItemTile(item: item),
],
);
} else if (state is ItemsFailure) {
return Center(
child: Text(
'Unexpected Failure, Cannot Load Items',
style: Theme.of(context).textTheme.headline6,
),
);
}
},
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: CartButton(),
),
],
),
),
);
}
}
class CartButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<CartBloc, CartState>(
// ignore: missing_return
builder: (context, state) {
if (state is CartLoading) {
return SizedBox.shrink();
} else if (state is CartLoaded) {
print('Cart Loaded');
return TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ListPage(
items: state.itemData.items,
);
}),
);
},
child: Text('Go To Cart'),
);
} else if (state is CartFailure) {
return Center(
child: Text(
'Unexpected Failure, Cannot Load Cart',
style: Theme.of(context).textTheme.headline6,
),
);
}
},
);
}
}
item_tile.dart
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:demo/item_model.dart';
import 'package:flutter/material.dart';
class ItemTile extends StatefulWidget {
final Item item;
const ItemTile({Key key, this.item}) : super(key: key);
@override
_ItemTileState createState() => _ItemTileState();
}
class _ItemTileState extends State<ItemTile> {
bool active;
@override
void initState() {
super.initState();
active = widget.item.active;
}
void _onItemTap() {
setState(() {
active = !active;
});
getIt<ItemsBloc>()
.add(UpdateItem(item: widget.item.copyWith(active: active)));
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.item.title),
tileColor: active ? Colors.redAccent : Colors.blueAccent,
onTap: () {
_onItemTap();
},
);
}
}
item_page.dart
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:flutter/material.dart';
import 'item_model.dart';
class ListPage extends StatefulWidget {
final List<Item> items;
const ListPage({Key key, this.items}) : super(key: key);
@override
_PlayerScreenState createState() => _PlayerScreenState();
}
class _PlayerScreenState extends State<ListPage> {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
'Cart List Page',
style: Theme.of(context).textTheme.headline6,
),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.separated(
itemCount: widget.items.length,
separatorBuilder: (BuildContext context, int index) =>
Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
trailing: IconButton(
icon: Icon(
Icons.close_rounded,
color: Colors.black,
),
onPressed: () {
getIt<ItemsBloc>().add(UpdateItem(
item: widget.items.elementAt(index).copyWith(
active: !widget.items.elementAt(index).active),
));
setState(() {
widget.items.removeAt(index);
});
if (widget.items.isEmpty) {
Navigator.pop(context);
}
},
),
title: Text(
widget.items.elementAt(index).title,
),
);
},
),
),
],
),
),
),
);
}
}
get_it.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/repository.dart';
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
Future<void> init() async {
// Bloc
getIt.registerLazySingleton<CartBloc>(() => CartBloc(itemRepository: getIt()));
getIt.registerFactory(() => ItemsBloc(
itemRepository: getIt(),
cartBloc: getIt(),
));
// Repository
getIt.registerSingleton<ItemRepository>(ItemRepository());
}
main.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'get_it.dart' as di;
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
di.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => getIt<ItemsBloc>()..add(GetItems()),
),
BlocProvider(
create: (context) => getIt<CartBloc>(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
),
);
}
}
下面是问题的图片:
【问题讨论】:
-
当您期望颜色发生变化时,您是否在控制台中看到您的
print('Items Loaded');语句?如果没有,那么BlocBuilder不会在您想要的时候重建。一旦你的ItemsLoaded状态产生,它应该用更新的颜色重建整个瓷砖列表。 -
当应用程序启动时,它在控制台中显示为 (I/flutter (4923): Items Loaded),当您选择一个项目时它显示 (I/flutter (4923): Cart Loaded)。是的,正如您所说,当我们从 ItemPage 中删除项目时,它没有显示 Items Loaded。这就是问题所在。
-
我在 Items_bloc 文件中的 _mapGetItemsToState 作为 print('Yielded') 中的 yield ItemsLoaded 之后添加了一行。它在移除项目时打印该行。所以状态已经产生,但不知何故,我认为 bloc 将带有更新项目的新状态视为前一个状态,这就是为什么不再调用 BlocBuilder 函数的原因。我不知道如何解决这个问题。请帮忙。
标签: flutter user-interface dart bloc flutter-bloc