Flutter 颤振:修改脚手架以跟踪当前显示的脚手架';应用程序范围SnackBar方法的构建上下文
我试图跟踪当前的Flutter 颤振:修改脚手架以跟踪当前显示的脚手架';应用程序范围SnackBar方法的构建上下文,flutter,dart,snackbar,Flutter,Dart,Snackbar,我试图跟踪当前的Scaffolds(它们的BuildContexts),以便创建应用程序范围的SnackBar函数。目前,我正在创建一个类,该类显示一个脚手架,并将其上下文添加到另一个类,该类管理当前运行的脚手架。然而,我没有成功,因为我目前的尝试有两个问题: 它无法正确存储当前的脚手架s 显然,dispose方法对于从当前Scaffold的列表中删除Scaffold的BuildContext来说太晚了,因此这给我带来了异常,“查找停用小部件的祖先是不安全的。” 以下是我目前的尝试: 实现(ma
Scaffold
s(它们的BuildContext
s),以便创建应用程序范围的SnackBar
函数。目前,我正在创建一个类
,该类显示一个脚手架
,并将其上下文
添加到另一个类
,该类管理当前运行的脚手架
。然而,我没有成功,因为我目前的尝试有两个问题:
脚手架
sdispose
方法对于从当前Scaffold
的列表中删除Scaffold
的BuildContext
来说太晚了,因此这给我带来了异常,“查找停用小部件的祖先是不安全的。
”
main.dart
):承认它不能像现在这样工作,它看起来也有点冗长和混乱。我想做的是简单地制作一个
Scaffold
类
,它的功能类似于Scaffold
,但与一个管理器类
,该管理器跟踪所有Scaffold
上下文以便我可以轻松地显示SnackBar
消息,无论用户在哪个页面上。我自己还没有尝试过这段代码,但是您如何创建一个ChangeNotifier
,与提供者一起使用,并将其附加到材质PP
上方:
class MyErrorChangeNotifier extends ChangeNotifier {
String error;
setError(String error) {
this.error = error;
notifyListeners();
}
}
然后创建一个自定义的Scaffold
,当您调用setError
时,如果安装了Scaffold
,它将显示一个SnackBar
:
class MyScaffold extends Scaffold {
// TODO constructor
@override
ScaffoldState createState() => MyScaffoldState();
}
class MyScaffoldState extends ScaffoldState {
MyErrorChangeNotifier _myErrorCN;
Function() _listener;
@override
void initState() {
super.initState();
_listener = () {
if (mounted) {
showSnackBar(SnackBar(content: Text(_myErrorCN.error)));
}
};
Future.microtask(() {
_myErrorCN = Provider.of<MyErrorChangeNotifier>(context, listen: false)..addListener(_listener);
});
}
@override
void dispose() {
_myErrorCN?.removeListener(_listener);
super.dispose();
}
}
类MyScaffold扩展了Scaffold{
//TODO构造函数
@凌驾
ScaffoldState createState()=>MyScaffoldState();
}
类MyScaffoldState扩展了ScaffoldState{
MyErrorChangeNotifier\u myErrorCN;
函数()\u侦听器;
@凌驾
void initState(){
super.initState();
_侦听器=(){
如果(已安装){
showSnackBar(SnackBar(内容:Text(_myErrorCN.error));
}
};
Future.microtask(){
_myErrorCN=Provider.of(context,listen:false)…addListener(_listener);
});
}
@凌驾
无效处置(){
_myErrorCN?.RemovelListener(_listener);
super.dispose();
}
}
每当您遇到这样的用例时,请尝试使用提供程序来考虑解决方案—数据将沿上下文层次结构发送到ChangeNotifier,然后ChangeNotifier将数据沿上下文层次结构发送回所有侦听它的小部件。多亏了Ovidiu的帮助,我找到了一个很好的答案。我决定放弃
提供程序
,转而使用静态类。我认为这样的实现更简单。此外,我还让它在新页面上显示一个SnackBar
(如果打开了新的Scaffold
)
要实现它,只需导入mscapfold.dart
并将Scaffold
更改为mscapfold
s即可
我发现这种方法非常方便,非常有用,它也可以帮助其他一些人,他们也在寻找一种简单的方法来显示SnackBar
,无论当前显示的是哪个Scaffold
以下是演示页面:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'MScaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Snackbar manager',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Snackbar manager'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return MScaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: 0,
child: Icon(Icons.add_circle_outline),
onPressed: () {
ShowSnackBar().showText("You were on page 1");
},
),
FloatingActionButton(
heroTag: 1,
child: Icon(Icons.remove_circle_outline),
onPressed: () {
ShowSnackBar().hide();
},
),
FloatingActionButton(
heroTag: 2,
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SecondScaffold();
}));
},
),
],
),
);
}
}
class SecondScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MScaffold(
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: 0,
child: Icon(Icons.add_circle_outline),
onPressed: () {
ShowSnackBar().show(
SnackBar(
content: Text("You were on page 2"),
),
);
},
),
FloatingActionButton(
heroTag: 1,
child: Icon(Icons.remove_circle_outline),
onPressed: () {
ShowSnackBar().hide();
},
),
FloatingActionButton(
heroTag: 2,
child: Icon(Icons.remove),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
}
}
这似乎将大大简化应用程序的设计。仅仅使用
Scaffold.of
有什么不对?“为了创建应用程序范围的SnackBar功能”为什么?什么会触发显示快餐店的需要?对于与连接相关的服务器消息,SnackBar
应该显示,而不管当前显示的是什么页面。以正常的方式使用它需要为每个Scaffold
提供者创建不同的方法,这对我来说仍然有点困惑。我尝试了您的代码,并将MaterialApp
指定为Provider
的子级,即Provider(create:(\u)=>MyErrorChangeNotifier(),child:MaterialApp(…
),我现在收到错误,“未处理的异常:尝试将Provider与Listenable/Stream(MyErrorChangeNotifier)的子类型一起使用。”.
,“建议我使用ListenableProvider
,ChangeNotifierProvider
,ValueListenableProvider
,或StreamProvider
我通过更改提供程序(创建:(\u)=>MyErrorChangeNotifier(),子项:MaterialApp)解决了上一个问题(…
toChangeNotifierProviderUsingChangeNotifierProvider
在您的情况下是正确的。当您说什么都没有发生时,您的意思是没有触发自定义支架连接的侦听器吗?尝试在构建MaterialApp
,并与之一起使用ChangeNotifierProvider.value
构造函数,以确保您没有重建不同的提供程序。如果在notifyListeners()上放置断点
,你应该能够检查这个
并查看附加了多少侦听器-如果你替换了所有的脚手架,应该有>=1个。我发现它正在工作,只是showcoldfold()
没有做任何事情。使用Scaffold
的上下文
,通过在生成器中包装Scaffold
获得(…
并将变量设置为其上下文
,我可以让SnackBar
显示。但是,我发现我没有办法隐藏SnackBar
变更通知程序
只提供一个可能的通知,没有任何变量。因此,我创建了两个变更通知程序
属性:showNot和hideNotifier
,并使用它们通知侦听器
class MyScaffold extends Scaffold {
// TODO constructor
@override
ScaffoldState createState() => MyScaffoldState();
}
class MyScaffoldState extends ScaffoldState {
MyErrorChangeNotifier _myErrorCN;
Function() _listener;
@override
void initState() {
super.initState();
_listener = () {
if (mounted) {
showSnackBar(SnackBar(content: Text(_myErrorCN.error)));
}
};
Future.microtask(() {
_myErrorCN = Provider.of<MyErrorChangeNotifier>(context, listen: false)..addListener(_listener);
});
}
@override
void dispose() {
_myErrorCN?.removeListener(_listener);
super.dispose();
}
}
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'MScaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Snackbar manager',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Snackbar manager'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return MScaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: 0,
child: Icon(Icons.add_circle_outline),
onPressed: () {
ShowSnackBar().showText("You were on page 1");
},
),
FloatingActionButton(
heroTag: 1,
child: Icon(Icons.remove_circle_outline),
onPressed: () {
ShowSnackBar().hide();
},
),
FloatingActionButton(
heroTag: 2,
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SecondScaffold();
}));
},
),
],
),
);
}
}
class SecondScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MScaffold(
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: 0,
child: Icon(Icons.add_circle_outline),
onPressed: () {
ShowSnackBar().show(
SnackBar(
content: Text("You were on page 2"),
),
);
},
),
FloatingActionButton(
heroTag: 1,
child: Icon(Icons.remove_circle_outline),
onPressed: () {
ShowSnackBar().hide();
},
),
FloatingActionButton(
heroTag: 2,
child: Icon(Icons.remove),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class ShowSnackBar extends ChangeNotifier {
SnackBar currentSnackBar;
int lastHideTime = -1; //in millisecondsSinceEpoch
String _msg;
bool isSnackBarVisible = false;
static final _thisClass = ShowSnackBar._internal();
ShowSnackBar._internal();
factory ShowSnackBar() {
return _thisClass;
}
ChangeNotifier showNotifier = ChangeNotifier();
ChangeNotifier hideNotifier = ChangeNotifier();
showText(String inputMsg) {
_msg = inputMsg;
currentSnackBar = SnackBar(content: Text(_msg));
isSnackBarVisible = true;
showNotifier.notifyListeners();
}
show(SnackBar inputSnackBar) {
currentSnackBar = inputSnackBar;
isSnackBarVisible = true;
showNotifier.notifyListeners();
}
hide() {
hideNotifier.notifyListeners();
}
}
class MScaffold extends Scaffold {
ValueKey key;
var appBar;
var body;
var floatingActionButton;
var floatingActionButtonLocation;
var floatingActionButtonAnimator;
var persistentFooterButtons;
var drawer;
var endDrawer;
var bottomNavigationBar;
var bottomSheet;
var backgroundColor;
var resizeToAvoidBottomPadding;
var resizeToAvoidBottomInset;
var primary;
var drawerDragStartBehavior;
var extendBody;
var extendBodyBehindAppBar;
var drawerScrimColor;
var drawerEdgeDragWidth;
MScaffold({
this.key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null),
assert(
!((key!=null
&&key.value is Map<String,dynamic>)
&&key.value.length==1
&&key.value.containsKey('MScaffoldAutoKey')),
"The Key you use for MScaffold cannot be a Map object that contains only one index "
"named, 'MScaffoldAutoKey,' as this is reserved for MScaffold."
),
super(key: key) {
if (key == null) this.key = _autoKeyGen();
}
static List<Key> _autoKeys = [];
bool _usesAutoKey = false;
Key _autoKeyGen() {
_usesAutoKey = true;
Key retKey = ValueKey({'MScaffoldAutoKey': _autoKeys.length});
_autoKeys.add(retKey);
return retKey;
}
@override
MScaffoldState createState() {
return MScaffoldState(
key: key,
appBar: appBar,
body: body,
floatingActionButton: floatingActionButton,
floatingActionButtonLocation: floatingActionButtonLocation,
floatingActionButtonAnimator: floatingActionButtonAnimator,
persistentFooterButtons: persistentFooterButtons,
drawer: drawer,
endDrawer: endDrawer,
bottomNavigationBar: bottomNavigationBar,
bottomSheet: bottomSheet,
backgroundColor: backgroundColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
primary: primary,
drawerDragStartBehavior: drawerDragStartBehavior,
extendBody: extendBody,
extendBodyBehindAppBar: extendBodyBehindAppBar,
drawerScrimColor: drawerScrimColor,
drawerEdgeDragWidth: drawerEdgeDragWidth,
autoKeys: _usesAutoKey ? _autoKeys : null,
);
}
}
class MScaffoldState extends ScaffoldState {
Key key;
var appBar;
var body;
var floatingActionButton;
var floatingActionButtonLocation;
var floatingActionButtonAnimator;
var persistentFooterButtons;
var drawer;
var endDrawer;
var bottomNavigationBar;
var bottomSheet;
var backgroundColor;
var resizeToAvoidBottomPadding;
var resizeToAvoidBottomInset;
var primary;
var drawerDragStartBehavior;
var extendBody;
var extendBodyBehindAppBar;
var drawerScrimColor;
var drawerEdgeDragWidth;
var autoKeys;
MScaffoldState({
this.key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
this.autoKeys,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null);
Function() _listenerShow;
Function() _listenerHide;
@override
void initState() {
super.initState();
_listenerShow = () {
if (mounted) {
Scaffold.of(_scaffoldContext)
.showSnackBar(ShowSnackBar().currentSnackBar)
.closed
.then((SnackBarClosedReason reason) {
ShowSnackBar().isSnackBarVisible = false;
ShowSnackBar().lastHideTime = DateTime.now().millisecondsSinceEpoch;
});
}
};
_listenerHide = () {
if (mounted) {
Scaffold.of(_scaffoldContext).hideCurrentSnackBar();
}
};
Future.microtask(() {
if (ShowSnackBar().isSnackBarVisible) _listenerShow();
ShowSnackBar().showNotifier.addListener(_listenerShow);
ShowSnackBar().hideNotifier.addListener(_listenerHide);
});
}
@override
dispose() {
ShowSnackBar().showNotifier?.removeListener(_listenerShow);
ShowSnackBar().hideNotifier?.removeListener(_listenerHide);
autoKeys?.remove(key);
super.dispose();
}
BuildContext _scaffoldContext;
@override
Widget build(BuildContext context) {
return Scaffold(
key: key,
appBar: appBar,
body: Builder(
builder: (context) {
_scaffoldContext = context;
return body;
},
),
floatingActionButton: floatingActionButton,
floatingActionButtonLocation: floatingActionButtonLocation,
floatingActionButtonAnimator: floatingActionButtonAnimator,
persistentFooterButtons: persistentFooterButtons,
drawer: drawer,
endDrawer: endDrawer,
bottomNavigationBar: bottomNavigationBar,
bottomSheet: bottomSheet,
backgroundColor: backgroundColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
primary: primary,
drawerDragStartBehavior: drawerDragStartBehavior,
extendBody: extendBody,
extendBodyBehindAppBar: extendBodyBehindAppBar,
drawerScrimColor: drawerScrimColor,
drawerEdgeDragWidth: drawerEdgeDragWidth,
);
}
}