flutter 使用Riverpod -如何正确实现自动刷新/重建功能?

5vf7fwbs  于 2022-12-14  发布在  Flutter
关注(0)|答案(3)|浏览(480)

我有一个简单的应用程序与Task的列表。每个Task是时间限制。

@freezed
class Task with _$Task {
  const factory Task({
    @Default('') String title,
    required DateTime start,
    required DateTime end,
  }) = _Task;
}

然后,我使用提供程序显示任务,例如:

class TasksController extends StateNotifier<List<Task>> {
  TasksController()
      : super(const []) {
    init();
  }

  Future<void> init() async {
    state = await GetTasksFromSomeRepo();
  }
}

我希望显示指示器,指示任务正在执行,例如:

有一个简单的解决方案,我每1秒重建Task的小部件的整个列表,使用:

Timer.periodic(Duration(seconds: 1), (Timer t) {
    setState(() {
        // this calls build method
    });
});

并且在build方法本身中,我检查DateTime.now()是否在每个Task的“开始/结束”边界内。

问题:

1.如何以更有效的方式来做这件事,最好是在Riverpod的帮助下?

nle07wnf

nle07wnf1#

使用setState的更高效的解决方案,这里TaskContainer小部件最多重建两次。

class TaskContainer extends StatefulWidget {
  const TaskContainer({
    required this.task,
    Key? key,
  }) : super(key: key);

  @override
  State<TaskContainer> createState() => _TaskContainerState();

  final Task task;
}

class _TaskContainerState extends State<TaskContainer> {
  bool _inProgress = false;

  void updateProgress() {
    final now = DateTime.now();

    setState(() {
      _inProgress = now.compareTo(widget.task.start) >= 0 &&
          now.compareTo(widget.task.end) <= 0;
    });
  }

  @override
  void initState() {
    super.initState();

    updateProgress();

    final nowFromEpoch = DateTime
        .now()
        .microsecondsSinceEpoch;
    final timeToStart = widget.task.start.microsecondsSinceEpoch - nowFromEpoch;
    final timeToEnd = widget.task.end.microsecondsSinceEpoch - nowFromEpoch;

    if (timeToStart > 0) {
      Future.delayed(Duration(microseconds: timeToStart), updateProgress);
    }
    if (timeToEnd > 0) {
      Future.delayed(Duration(microseconds: timeToEnd), updateProgress);
    }
  }

  @override
  Widget build(BuildContext context) {
    final task = widget.task;
    final formatter = DateFormat('HH:mm');

    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: Colors.black),
      ),
      padding: const EdgeInsets.all(10),
      child: Column(
        children: [
          Row(
            children: [
              Text(task.title),
              const SizedBox(
                width: 20,
              ),
              if (_inProgress) const Text('In Progress...'),
            ],
          ),
          Row(
            children: [
              Text(formatter.format(task.start)),
              const SizedBox(
                width: 20,
              ),
              Text(formatter.format(task.end)),
            ],
          )
        ],
      ),
    );
  }
}

测试它的示例页面。

class Home extends StatelessWidget {
  const Home({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final now = DateTime.now();
    final repository = <Task>[
      Task(
        title: 'First',
        start: now,
        end: DateTime(now.year, now.month, now.day, now.hour, now.minute + 1),
      ),
      Task(
        title: 'Second',
        start: DateTime(now.year, now.month, now.day, now.hour, now.minute + 1),
        end: DateTime(now.year, now.month, now.day, now.hour, now.minute + 2),
      ),
    ];

    return Scaffold(
      body: ListView.separated(
        itemBuilder: (context, index) => TaskContainer(task: repository[index]),
        separatorBuilder: (context, index) =>
        const SizedBox(
          height: 10,
        ),
        itemCount: repository.length,
      ),
    );
  }
}

编辑:使用Riverpod实施

您可以通过使用一系列将任务作为参数的提供程序来实现同样的目的。您的Task类应该是不可变的,并且应该覆盖== operatorhashcode
提供者:

final taskProgressingProvider = StateNotifierProvider.family<TaskProgressingNotifier, bool, Task>((ref, task) {
  return TaskProgressingNotifier(task);
});

class TaskProgressingNotifier extends StateNotifier<bool> {
  TaskProgressingNotifier(this.task) : super(false) {
    updateProgress();

    final nowFromEpoch = DateTime.now().microsecondsSinceEpoch;
    final timeToStart = task.start.microsecondsSinceEpoch - nowFromEpoch;
    final timeToEnd = task.end.microsecondsSinceEpoch - nowFromEpoch;

    if (timeToStart > 0) {
      Future.delayed(Duration(microseconds: timeToStart), updateProgress);
    }
    if (timeToEnd > 0) {
      Future.delayed(Duration(microseconds: timeToEnd), updateProgress);
    }
  }

  final Task task;

  void updateProgress() {
    final now = DateTime.now();

    state = now.compareTo(task.start) >= 0 && now.compareTo(task.end) <= 0;
  }
}

TaskContainer转换为ConsumerWidget

class TaskContainer extends ConsumerWidget {
  const TaskContainer({
    required this.task,
    Key? key,
  }) : super(key: key);

  final Task task;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final formatter = DateFormat('HH:mm');
    final inProgress = ref.watch(taskProgressingProvider(task));

    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: Colors.black),
      ),
      padding: const EdgeInsets.all(10),
      child: Column(
        children: [
          Row(
            children: [
              Text(task.title),
              const SizedBox(
                width: 20,
              ),
              if (inProgress) const Text('In Progress...'),
            ],
          ),
          Row(
            children: [
              Text(formatter.format(task.start)),
              const SizedBox(
                width: 20,
              ),
              Text(formatter.format(task.end)),
            ],
          )
        ],
      ),
    );
  }
}
xggvc2p6

xggvc2p62#

任务解析器.dart:

enum TaskEvent {
      onProgrees,
      completed,
    }
    
    class TaskResolver {
    
      final Ref ref;
      TaskResolver(this.ref);
    
      Stream<TaskEvent> taskEvent() async* {

    // get from your provider,
    //ref.watch(taskController);

    //for simulation i just assign next day
    final taskDateCompleted = DateTime(2022, 12, 8);
    TaskEvent? current;
    while (true){
      final now = DateTime.now();

      if(current == null){
        //default value

        current = TaskEvent.onProgrees;
        yield current;
      }
      if(taskDateCompleted.isBefore(now) && current != TaskEvent.onProgrees){
        current = TaskEvent.onProgrees;
        yield TaskEvent.onProgrees;
      }
      if(taskDateCompleted.isAfter(now) && current != TaskEvent.completed){
        current = TaskEvent.completed;
        yield TaskEvent.completed;
      }
      await Future.delayed(const Duration(seconds: 1));
    }

  }

}
    
    final taskResolver = StreamProvider.autoDispose<TaskEvent>((ref) => TaskResolver(ref).taskEvent());

在UI.dart上:

class TaskPage extends ConsumerWidget {
  const TaskPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, ref) {
    final status = ref.watch(taskResolver);
    return status.when(
        data: (taskEvent){
          print(taskEvent);
          return Text(taskEvent.name);
        },
        error: (_,__)=> const Text('error'),
        loading: ()=> const Text('loading')
    );

  }
}
4xrmg8kj

4xrmg8kj3#

因为您不与任何API通信,所以检查正在进行的事情的唯一方法是,正如您在问题中提到的,使用计时器。
但是,您可以做的是,通过访问TasksController来更改task中的某些内容(您可以创建一个新变量state),而不是调用setState
如果实现正确,应该会触发状态更改,只重建正确的小部件,而不是整个小部件树。

相关问题