在提供程序流更改之间进行动画处理
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());
}
两个问题:
我想为两种状态之间的过渡制作动画,即。已连接和已断开连接,断开连接屏幕从显示屏顶部向下流动,反之亦然。使用提供者状态管理来做到这一点的正确方法是什么?现在它只是瞬间重建,这不是很 'pretty'。
由于 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'),
),
);
}
}
我有 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());
}
两个问题:
我想为两种状态之间的过渡制作动画,即。已连接和已断开连接,断开连接屏幕从显示屏顶部向下流动,反之亦然。使用提供者状态管理来做到这一点的正确方法是什么?现在它只是瞬间重建,这不是很 'pretty'。
由于 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'),
),
);
}
}