我想启动一个在 MyChangeNotifier 更改时返回 future 的函数。
使用未来的构建器小部件并直接调用该函数可以工作,但这也会在重建小部件时调用该函数,从而导致对我的函数的不必要的调用。该函数应该像 init 函数一样在 build 方法之外调用,但仅在更新 MyChangeNotifier 时调用。
我没有找到解决这个问题的方法。 在 init 函数中,没有构建上下文来访问 MyChangeNotifier 并添加侦听器。似乎要访问此构建上下文,您需要使用构建方法,然后强制您在每次小部件重建时被调用。
我想念什么?你有一个优雅的解决方案来解决这个问题吗?
我希望这个 speedo 代码可以帮助您更好地理解我的问题。
MyChangeNotifier extends ChangeNotifier {
//contains a list of directories that can be updated from anywhere in the app
}
MyWidget extend StatelessWidget { // probably some states are needed
Widget build(BuildContext context) {
var myChangeNotifier = Provider.of<MyChangeNotifier>(context);
return FutureBuilder(future: _computemd5(myChangeNotifier), builder: return Text(futur_result);); // the future is computed every time the build method is called not only when MyChangeNotifier changed.
}
future<String> _computemd5(MyChangeNotifier myChangeNotifier) {
// compute
}
}
最优雅的解决方案是使用具有相对较高学习曲线的外部包 - Riverpod。
Riverpod 是一个更强大的 Provider 版本,它允许您在整个应用程序范围内拥有持久的异步函数。
从他们的网站来看,Riverpod 的动机部分是为了回答你的确切问题:
异步请求需要本地缓存,因为每当 UI 更新时重新执行它们是不合理的。
要真正解决您的问题,请使用 AsyncNotifierProvider,如下所示:
注意:这使用了 Riverpod 代码生成器,所以你必须
@riverpod
class AsyncTodos extends _$AsyncTodos {
Future<List<Todo>> _fetchTodo() async {
final json = await http.get('api/todos');
final todos = jsonDecode(json) as List<Map<String, dynamic>>;
return todos.map(Todo.fromJson).toList();
}
@override
FutureOr<List<Todo>> build() async {
// Load initial todo list from the remote repository
return _fetchTodo();
}
Future<void> addTodo(Todo todo) async {
// Set the state to loading
state = const AsyncValue.loading();
// Add the new todo and reload the todo list from the remote repository
state = await AsyncValue.guard(() async {
await http.post('api/todos', todo.toJson());
return _fetchTodo();
});
}
// Let's allow removing todos
Future<void> removeTodo(String todoId) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await http.delete('api/todos/$todoId');
return _fetchTodo();
});
}
// Let's mark a todo as completed
Future<void> toggle(String todoId) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await http.patch(
'api/todos/$todoId',
<String, dynamic>{'completed': true},
);
return _fetchTodo();
});
}
}
每当结果发生变化时,构建方法(您要调用的函数)就会执行。
您可以组合使用
StatefulWidget
和 ListenableBuilder
。确保您的小部件是 StatefulWidget
,因为您需要管理状态和生命周期方法。然后,使用 ListenableBuilder
聆听 MyChangeNotifier
的变化。仅当通知程序通知其侦听器时,此小部件才会重建,这正是您所需要的。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Future<String>? _futureMd5;
@override
void initState() {
super.initState();
// Initialize _futureMd5 with a default value or null if not needed
_futureMd5 = _computemd5(Provider.of<MyChangeNotifier>(context, listen: false));
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: Provider.of<MyChangeNotifier>(context),
builder: (context, child) {
// Trigger _computemd5 only when MyChangeNotifier is updated
_futureMd5 = _computemd5(Provider.of<MyChangeNotifier>(context, listen: false));
return FutureBuilder<String>(
future: _futureMd5,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // Show loading indicator while waiting
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text(snapshot.data!); // Display the result
}
},
);
},
);
}
Future<String> _computemd5(MyChangeNotifier myChangeNotifier) async {
// Your computation logic here
// For demonstration, let's just return a dummy string after a delay
await Future.delayed(Duration(seconds: 2));
return 'MD5 Result';
}
}