如何正确开始和结束或动画和从其他 class flutter 更改文本

How to correctly start and end or animation and changing text from other class flutter

我正在学习 flutter,我想开始动画并从 AlertDialog 响应(基本上来自其他 class)设置应用栏标题 'syncing',然后结束动画并在异步操作后再次设置标题。

所以目前我正在使用 GlobalKey 和 Riverpod(StateNotifier) 实现这一点。 创建了 MainScreen GlobalKey 并在我调用

的异步操作之前使用其他 class 的 GlobalKey
mainScreenScaffoldKey.currentState.context
      .read(syncProgressProvider)
      .setSyncing();

并在异步操作后结束动画:

 mainScreenScaffoldKey.currentState.context
      .read(syncProgressProvider)
      .syncProgressDone();

代码:

Map<String, dynamic> dialogResponse = await showDialog(
    context: context,
    builder: (context) => EditNoteScreen(
          _index,
          _task,
          _color,
          dateTime,
          priority: priority,
        ));
if (dialogResponse != null) {
  mainScreenScaffoldKey.currentState.context
      .read(syncProgressProvider)
      .setSyncing();
  await SaveToLocal().save(context.read(listStateProvider.state));
  await CloudNotes().updateCloudNote(
    task: dialogResponse["task"],
    priority: dialogResponse["priority"],
    dateTime: dateTime.toString(),
    index: dialogResponse["index"],
  );
  mainScreenScaffoldKey.currentState.context
      .read(syncProgressProvider)
      .syncProgressDone();
}

和 AppBar 标题中的监听变量 属性 在 MainScreen

我觉得这种方法不对吗?

这里有一些额外的片段 syncProgressProiver:

 class SyncProgressModel extends StateNotifier<bool>{

 SyncProgressModel() : super(false);

 syncProgressDone(){
   state =false;
 }
 setSyncing(){
   state =true;
 }

MainScreen AppBar 标题

Consumer(
      builder: (context, watch, child) {
        var syncProgress = watch(syncProgressProvider.state);
        if (!syncProgress) {
          return const Text('To-Do List');
        } else {
          return Row(
            children: [
              const Text('Syncing..'),
              Container(
                margin: const EdgeInsets.only(left: 10),
                width: 25,
                height: 25,
                child: CircularProgressIndicator(
                  strokeWidth: 2,
                  valueColor: animColors,
                ),
              )
            ],
          );
        }
      },
    ),

像这样

我对你的动画一无所知(你实际上并没有分享任何逻辑或你指的是什么 initState )但是如果你唯一想要的是为 CircularProgressIndicator 的颜色设置动画那么你可以只创建一个 StatefulWidget 来为您执行此操作,并仅在 syncProgress == true

时调用它来构建
class AnimatedWidget extends StatefulWidget {
  AnimatedWidget({Key key}) : super(key: key);

  @override
  _AnimatedWidgetState createState() => _AnimatedWidgetState();
}

class _AnimatedWidgetState extends State<AnimatedWidget>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  final Animatable<Color> _colorTween = TweenSequence<Color>([
    TweenSequenceItem<Color>(
      tween: ColorTween(begin: Colors.red, end: Colors.amber),
      weight: 20,
    ),
    TweenSequenceItem<Color>(
      tween: ColorTween(begin: Colors.amber, end: Colors.green),
      weight: 20,
    ),
    TweenSequenceItem<Color>(
      tween: ColorTween(begin: Colors.green, end: Colors.blue),
      weight: 20,
    ),
    TweenSequenceItem<Color>(
      tween: ColorTween(begin: Colors.blue, end: Colors.purple),
      weight: 20,
    ),
    TweenSequenceItem<Color>(
      tween: ColorTween(begin: Colors.purple, end: Colors.red),
      weight: 20,
    ),
    
  ]).chain(CurveTween(curve: Curves.linear));

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 5),
      animationBehavior: AnimationBehavior.preserve,
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        const Text('Syncing..'),
        Container(
          margin: const EdgeInsets.only(left: 10),
          width: 25,
          height: 25,
          child: CircularProgressIndicator(
            strokeWidth: 2,
            valueColor: _colorTween.animate(_controller)
          ),
        )
      ],
    );
  }
}

在您的消费者中调用它,小部件将自行处理动画

Consumer(
      builder: (context, watch, child) {
        var syncProgress = watch(syncProgressProvider.state);
        if (!syncProgress) {
          return const Text('To-Do List');
        } else {
          return AnimatedWidget(); //right here
        }
      },
    ),

更新

To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

这意味着您应该在重建上下文本身之前根据您的上下文保留对对象的引用(当您在对话框中调用 updateValueAt 或 updateValue 时,它​​会重建列表并且不再安全地调用context.read)

updateCloudNote(BuildContext context) async {
    /// keep the reference before calling the dialog to prevent 
    /// when the context change because of the dialogs action
    final syncProgress = context.read(syncProgressProvider); 
    final listState = context.read(listStateProvider.state);
    Map<String, dynamic> dialogResponse = await showDialog(
        context: context,
        builder: (context) => EditNoteScreen(
              _index,
              _task,
              _color,
              dateTime,
              priority: priority,
            ));
    if (dialogResponse != null) {
      //when using GlobalKey didn't get that Widget Ancestor error
      // use the reference saved instead of context.read
      syncProgress.setSyncing();
      await SaveToLocal().save(listState);
      await CloudNotes().updateCloudNote(
        task: dialogResponse["task"],
        priority: dialogResponse["priority"],
        dateTime: dateTime.toString(),
        index: dialogResponse["index"],
      );
      syncProgress.syncProgressDone();
    }
  }

你结合提供者所做的基本上就是你可以在同一个里面做的syncProgressProvider

final syncProgressProvider = StateNotifierProvider<SyncProgressModel>((ref)
    => SyncProgressModel(ref.read));

class SyncProgressModel extends StateNotifier<bool>{
 final saveToLocal saveLocal = SaveToLocal();
 final CloudNotes cloudNotes = CloudNotes();
 final Reader _read;

 SyncProgressModel(this._read) : super(false);

 syncProgressDone(){
   state = false;
 }

 setSyncing(){
   state = true;
 }

 updateCall({int priority, int index, String task, String dateTime}) async {
   state = true;
   await saveLocal.save(_read(listStateProvider.state));
   await cloudNotes.updateCloudNote(
      task: task,
      priority: priority,
      dateTime: dateTime,
      index: index,
   );
   state = false;
  }

}

最后结合这两个想法:

updateCloudNote(BuildContext context) async {
    /// keep the reference before calling the dialog to prevent 
    /// when the context change because of the dialogs action
    final syncProgress = context.read(syncProgressProvider); 
    Map<String, dynamic> dialogResponse = await showDialog(
        context: context,
        builder: (context) => EditNoteScreen(
              _index,
              _task,
              _color,
              dateTime,
              priority: priority,
            ));
    if (dialogResponse != null) {
      await syncProgress.updateCall(
        task: dialogResponse["task"],
        priority: dialogResponse["priority"],
        dateTime: dateTime.toString(),
        index: dialogResponse["index"],
      );
    }

 }