gpt4 book ai didi

flutter - 在 Flutter 中使用 Provider 更新和动画化 AnimatedList

转载 作者:行者123 更新时间:2023-12-05 01:57:28 24 4
gpt4 key购买 nike

我能够成功地为 AnimatedList 制作动画当列表数据存储在拥有列表小部件的同一组件中时,Flutter 中的内容(即,当列表数据发生更改时不会发生重建)。当我尝试从 ChangeNotifier 获取列表的项目时遇到问题使用 ProviderConsumer .

拥有 AnimatedList 的组件,我们称它为ListPage , 是用 Consumer<ListItemService> build 的.我的理解是 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),
),
);
}
}

最佳答案

您可以使用 didUpdateWidget 并检查新旧列表之间的差异。 “检查差异”意味着查看已删除与添加的内容。在你的情况下,Item 小部件应该有一些要识别的东西。例如,您可以使用 Equatable,这样 Item 之间的相等性就是它们属性之间的相等性。

另一个重要方面是您正在处理一个列表,它是可变的,但 Widget 应该是不可变的。因此,无论何时修改列表,实际上都创建了一个新列表,这一点至关重要。

下面是实现细节,最有趣的部分当然是评论(虽然渲染也很有趣 ;)):

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),
),
),
);
}
}

关于flutter - 在 Flutter 中使用 Provider 更新和动画化 AnimatedList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69311546/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com