当 MyChangeNotifier 更改时如何计算函数,而无需在构建方法中进行不必要的调用 - Flutter

问题描述 投票:0回答:2

我想启动一个在 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
    }
}
flutter state-management
2个回答
0
投票

最优雅的解决方案是使用具有相对较高学习曲线的外部包 - Riverpod。

Riverpod 是一个更强大的 Provider 版本,它允许您在整个应用程序范围内拥有持久的异步函数。

从他们的网站来看,Riverpod 的动机部分是为了回答你的确切问题:

异步请求需要本地缓存,因为每当 UI 更新时重新执行它们是不合理的。

要真正解决您的问题,请使用 AsyncNotifierProvider,如下所示:

注意:这使用了 Riverpod 代码生成器,所以你必须

  • 完全安装Riverpod
  • 将生成的代码所在的文件部分(部分“example.g.dart”)
  • 执行代码生成器(flutter pub run build_runner watch -d)
@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();
    });
  }
}

每当结果发生变化时,构建方法(您要调用的函数)就会执行。


0
投票

您可以组合使用

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';
 }
}
© www.soinside.com 2019 - 2024. All rights reserved.