在提供程序流更改之间进行动画处理

Animating between provider stream changes

我有 5 个页面的 flutter 综合浏览量,每个页面都有自己的脚手架。所有状态都通过流或值的提供者进行管理。我有一个流,它有自己的内置方法,可以通过 InternetConnected.connected 或断开连接。当互联网连接丢失时,我想在特定页面加载一个单独的 UI 来显示互联网连接丢失,而不是之前存在于脚手架中的小部件。

我现在是怎么做的(伪代码):

class ConnectionScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  final connection = Provider.of<InternetStatus>(context);

  detectConnection () {
  if (connection.InternetConnection == InternetConnection.connected)
      return Container(); // Returns UI when internet on
  else 
      return Container(); // Returns disconnected UI
  }

  return Scaffold(body: detectConnection());
}

两个问题:

  1. 我想为两种状态之间的过渡制作动画,即。已连接和已断开连接,断开连接屏幕从显示屏顶部向下流动,反之亦然。使用提供者状态管理来做到这一点的正确方法是什么?现在它只是瞬间重建,这不是很 'pretty'。

  2. 由于 Provider.of<> 不允许在流值发生变化的情况下进行粒度重建,我将如何做到这一点以便更好地处理提供商的其他属性 'provided'?我知道 Consumer 和 Selector,但它们也在重建 UI...

提前致谢

动画

AnimatedSwitcher 小部件(包含在 SDK 中,与 Provider 无关)可能足以在显示连接/断开状态的两个小部件之间进行动画处理。 (如果您只是在 Container 的构造函数参数列表中切换颜色或其他内容,AnimatedContainer 也可能有效。)

AnimatedSwitcher 的 child 需要 key 当 children 与 class 相同,但内部不同。如果它们是完全不同的类型,Flutter 知道在两者之间设置动画,但如果它们是相同的类型则不知道。 (与 Flutter 如何分析 widget 树以寻找所需的重建有关。)

仅重建受影响的小部件

在下面的示例中,YellowWidget 没有被重建,它的 parent 也没有。在示例中从连接状态更改为断开连接状态时,只有 Consumer<InternetStatus> 小部件正在重建。

我不是 Provider 方面的专家,我发现在知道使用哪个 Provider / Consumer / Selector / watcher 以避免不必要的重建时很容易出错。如果 Provider 没有为您点击,您可能对 Get, or RxDart+GetIt 等其他状态管理解决方案感兴趣。

注意:ChangeNotifierProvider 的 child 中使用了一个额外的 Builder 小部件作为 parent,使所有内容都在 child 之下。这允许 InheritedWidget 按预期运行(构建 Provider 的基础)。否则,ChangeNotifierProvider 的 child 实际上会共享其上下文并成为其兄弟,而不是后代。

即他们都会得到此处显示的上下文:

class ProviderGranularPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

这也是Flutter的一个棘手的细微差别。如果你wrap your entire MaterialApp or MyApp widget in Provider,这个额外的Builder显然是不必要的

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class InternetStatus extends ChangeNotifier {
  bool connected = true;

  void setConnect(bool _connected) {
    connected = _connected;
    notifyListeners();
  }
}

/// Granular rebuilds using Provider package
class ProviderGranularPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<InternetStatus>(
      create: (_) => InternetStatus(),
      child: Builder(
        builder: (context) {
          print('Page (re)built');
          return SafeArea(
            child: Scaffold(
              body: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Expanded(
                    flex: 3,
                    child: Consumer<InternetStatus>(
                      builder: (context, inetStatus, notUsed) {
                        print('status (re)built');
                        return AnimatedSwitcher(
                          duration: Duration(seconds: 1),
                          child: Container(
                              key: getStatusKey(context),
                              alignment: Alignment.center,
                              color: getStatusColor(inetStatus),
                              child: getStatusText(inetStatus.connected)
                          ),
                        );
                      },
                    ),
                  ),
                  Expanded(
                    flex: 3,
                    child: YellowWidget(),
                  ),
                  Expanded(
                    flex: 1,
                    child: Container(
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        children: [
                          RaisedButton(
                            child: Text('Connect'),
                            onPressed: () => setConnected(context, true),
                          ),
                          RaisedButton(
                            child: Text('Disconnect'),
                            onPressed: () => setConnected(context, false),
                          )
                        ],
                      ),
                    ),
                  )
                ],
              ),
            ),
          );
        },
      ),
    );
  }

  /// Show other ways to access Provider State, using context & Provider.of
  Key getStatusKey(BuildContext context) {
    return ValueKey(context.watch<InternetStatus>().connected);
  }

  void setConnected(BuildContext context, bool connected) {
    Provider.of<InternetStatus>(context, listen: false).setConnect(connected);
  }

  Color getStatusColor(InternetStatus status) {
    return status.connected ? Colors.blue : Colors.red;
  }

  Widget getStatusText(bool connected) {
    String _text = connected ? 'Connected' : 'Disconnected';
    return Text(_text, style: TextStyle(fontSize: 25));
  }
}

class YellowWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Yellow was (re)built');
    return Container(
      color: Colors.yellow,
      child: Center(
        child: Text('This should not rebuild'),
      ),
    );
  }
}