首页 > 解决方案 > 如何正确开始和结束或动画和更改其他类颤动的文本

问题描述

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

所以目前我正在使用 GlobalKey 和 Riverpod(StateNotifier) 来实现这一点。在我调用异步操作之前创建 MainScreen GlobalKey 并使用其他类中的 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();
}

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

我觉得这种做法不对,是吗?

这里有一些额外的片段 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,
                ),
              )
            ],
          );
        }
      },
    ),

像这样

在此处输入图像描述

标签: flutterriverpod

解决方案


我对您的动画一无所知(您实际上并没有共享任何逻辑或您指的是什么 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
        }
      },
    ),

更新

要在其 dispose() 方法中安全地引用小部件的祖先,请通过在小部件的 didChangeDependencies() 方法中调用dependOnInheritedWidgetOfExactType() 来保存对祖先的引用。

这意味着在重建上下文本身之前,您应该根据您的上下文保留对象的引用(当您在对话框中调用 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"],
      );
    }

 }

推荐阅读