我有一个页面视图,有5个页面,每个页面都有自己的脚手架。所有状态都通过流或值的提供者进行管理。我有一个流,它有自己的内置方法,通过InternetConnected.connected或disconnect。当互联网连接丢失时,我想在特定页面中加载一个单独的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());
}
两个问题:
我想设置两种状态之间的转换动画,即连接和断开连接,断开连接屏幕从显示器顶部向下流动,反之亦然。使用提供商状态管理的正确方法是什么?现在它只是瞬间重建,这不是很"漂亮"。
由于<gt;不允许在流值更改的情况下进行细粒度重建,我该如何使其更好地处理提供者"提供"的其他属性?我知道Consumer和Selector,但它们也在重建UI。。。
提前感谢
动画
AnimatedSwitcher
小部件(包含在SDK中,与Provider无关(可能足以在显示连接/断开状态的两个小部件之间进行动画处理。(如果你只是在Container的构造函数参数列表中切换一种颜色或其他东西,AnimatedContainer可能也能工作。(
当AnimatedSwitcher的子级属于同一类,但内部不同时,需要为其提供key
。如果它们是完全不同的类型,Flutter知道在两者之间设置动画,但如果它们是同一类型,则不知道。(这与Flutter如何分析小部件树以寻找所需的重建有关。(
仅重建受影响的小工具
在下面的示例中,YellowWidget没有被重建,它的父级也没有。在本例中,从"已连接"状态更改为"断开连接"状态时,仅重建Consumer<InternetStatus>
小部件。
我不是提供者方面的专家,我发现在知道使用哪个提供者/消费者/选择器/观察者以避免不必要的重建时很容易出错。如果提供商没有为您点击,您可能会对其他状态管理解决方案感兴趣,如Get或RxDart+GetIt等。
注意:一个额外的Builder小部件被用作ChangeNotificationProvider子级的父级,以使其下的所有内容都成为子级。这允许InheritedWidget按预期运行(构建Provider的基础(。否则,ChangeNotificationProvider的子代实际上将共享其上下文,并成为其兄弟,而不是后代。
即他们都会得到这里显示的上下文:
class ProviderGranularPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
这也是Flutter的一个微妙之处。如果您将整个MaterialApp或MyApp小部件包装在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'),
),
);
}
}