Bru*_*Ely 5 flutter flutter-provider
当列表数据存储在拥有列表小部件的同一组件中时,我能够成功地AnimatedList
在 Flutter 中对 的内容进行动画处理(即,当列表数据发生更改时不会发生重建)。当我尝试从ChangeNotifier
usingProvider
和获取列表中的项目时,我遇到了问题Consumer
。
拥有 的组件AnimatedList
(我们称之为ListPage
)是用 构建的Consumer<ListItemService>
。我的理解是,ListPage
每当服务更新列表数据并调用时就会重建notifyListeners()
。当发生这种情况时,我不确定ListPage
可以调用其中的哪个位置AnimatedListState.insertItem
来对列表进行动画处理,因为在build
列表状态期间仍然是null
。结果是一个列表,其内容没有动画效果。
我认为我的问题归结为“如果实时获取和更新内容,我如何管理此列表的状态?”,理想情况下,我想了解正在发生的事情,但我愿意接受有关如何我的建议如果这不是完成任务的最佳方式,则应该更改此设置。
下面是一些说明问题的代码:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<AuthService>(
create: (_) => AuthService(),
),
ChangeNotifierProxyProvider<AuthService, ListItemService>(
create: (_) => ListItemService(),
update: (_, authService, listItemService) =>
listItemService!..update(authService),
),
],
child: MaterialApp(
home: HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer<ListItemService>(
builder: (context, listItemService, _) =>
ListPage(items: listItemService.items),
);
}
}
// Implementation details aren't really relevant, but
// this only updates if the user logs in or out.
class AuthService extends ChangeNotifier {}
class ListItemService extends ChangeNotifier {
List<Item> _items = [];
List<Item> get items => _items;
Future<void> update(AuthService authService) async {
// Method that subscribes to a Firestore snapshot
// and calls notifyListeners() after updating _items.
}
}
class Item {
Item({required this.needsUpdate, required this.content});
final String content;
bool needsUpdate;
}
class ListPage extends StatefulWidget {
const ListPage({Key? key, required this.items}) : super(key: key);
final List<Item> items;
@override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
late int _initialItemCount;
@override
void initState() {
_initialItemCount = widget.items.length;
super.initState();
}
void _updateList() {
for (int i = 0; i < widget.items.length; i++) {
final item = widget.items[i];
if (item.needsUpdate) {
// _listKey.currentState is null here if called
// from the build method.
_listKey.currentState?.insertItem(i);
item.needsUpdate = false;
}
}
}
@override
Widget build(BuildContext context) {
_updateList();
return AnimatedList(
key: _listKey,
initialItemCount: _initialItemCount,
itemBuilder: (context, index, animation) => SizeTransition(
sizeFactor: animation,
child: Text(widget.items[index].content),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用didUpdateWidget
并检查新旧列表之间的差异。“检查差异”意味着查看删除的内容和添加的内容。在你的情况下,Item
小部件应该有一些可以识别的东西。Equatable
例如,您可以使用Item
s 之间的相等性来表示它们的属性之间的相等性。
另一个重要的方面是您正在处理一个列表,它是可变的,但 Widgets 应该是不可变的。因此,每当您修改列表时,实际上都会创建一个新列表,这一点至关重要。
以下是实现细节,最有趣的部分当然是注释(尽管渲染也很有趣;)):
import 'dart:async';
import 'dart:math';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<AuthService>(
create: (_) => AuthService(),
),
ChangeNotifierProxyProvider<AuthService, ListItemService>(
create: (_) => ListItemService(),
update: (_, authService, listItemService) => listItemService!..update(authService),
),
],
child: MaterialApp(
home: HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: Consumer<ListItemService>(
builder: (context, listItemService, _) => ListPage(
// List.from is very important because it creates a new list instead of
// giving the old one mutated
items: List.from(listItemService.items),
),
),
),
);
}
}
// Implementation details aren't really relevant, but
// this only updates if the user logs in or out.
class AuthService extends ChangeNotifier {}
class ListItemService extends ChangeNotifier {
List<Item> _items = [];
List<Item> get items => _items;
Future<void> update(AuthService authService) async {
// Every 5 seconds
Timer.periodic(Duration(seconds: 5), (timer) {
// Either create or delete an item randomly
if (Random().nextDouble() > 0.5 && _items.isNotEmpty) {
_items.removeAt(Random().nextInt(_items.length));
} else {
_items.add(
Item(
needsUpdate: true,
content: 'This is item with random number ${Random().nextInt(10000)}',
),
);
}
notifyListeners();
});
}
}
class Item extends Equatable {
Item({required this.needsUpdate, required this.content});
final String content;
bool needsUpdate;
@override
List<Object?> get props => [content]; // Not sure you want to include needsUpdate?
}
class ListPage extends StatefulWidget {
const ListPage({Key? key, required this.items}) : super(key: key);
final List<Item> items;
@override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
final _listKey = GlobalKey<AnimatedListState>();
// You can use widget if you use late
late int _initialItemCount = widget.items.length;
/// Handles any removal of [Item]
_handleRemovedItems({
required List<Item> oldItems,
required List<Item> newItems,
}) {
// If an [Item] was in the old but is not in the new, it has
// been removed
for (var i = 0; i < oldItems.length; i++) {
final _oldItem = oldItems[i];
// Here the equality checks use [content] thanks to Equatable
if (!newItems.contains(_oldItem)) {
_listKey.currentState?.removeItem(
i,
(context, animation) => SizeTransition(
sizeFactor: animation,
child: Text(oldItems[i].content),
),
);
}
}
}
/// Handles any added [Item]
_handleAddedItems({
required List<Item> oldItems,
required List<Item> newItems,
}) {
// If an [Item] is in the new but was not in the old, it has
// been added
for (var i = 0; i < newItems.length; i++) {
// Here the equality checks use [content] thanks to Equatable
if (!oldItems.contains(newItems[i])) {
_listKey.currentState?.insertItem(i);
}
}
}
// Here you can check any update
@override
void didUpdateWidget(covariant ListPage oldWidget) {
super.didUpdateWidget(oldWidget);
_handleAddedItems(oldItems: oldWidget.items, newItems: widget.items);
_handleRemovedItems(oldItems: oldWidget.items, newItems: widget.items);
}
@override
Widget build(BuildContext context) {
return AnimatedList(
key: _listKey,
initialItemCount: _initialItemCount,
itemBuilder: (context, index, animation) => SizeTransition(
sizeFactor: animation,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(widget.items[index].content),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1880 次 |
最近记录: |