Flutter:为什么在构建函数中基于条件语句在脚手架之间进行切换,但不适用于自定义窗口小部件
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter:为什么在构建函数中基于条件语句在脚手架之间进行切换,但不适用于自定义窗口小部件相关的知识,希望对你有一定的参考价值。
我创建了一个自定义Scaffold
,以便无论显示哪个页面,都可以轻松显示SnackBar
消息,称为MScaffold
。但是,当我尝试有条件地构建两个可能的MScaffold
中的一个或另一个时,我会再次获得相同的MScaffold
。当我用常规的Scaffold
执行此操作时,页面切换正常,使我相信它与MScaffold
有关。不过,我不知道为什么。我唯一能猜到的是,当您扩展Widget
时,某些属性必须更改,但是我无法弄清楚为什么这会阻止MyHomePage
显示不同的MScaffold
(当再次显示时,不同的Scaffold
就可以了。
我还尝试了最大扩展名Scaffold
的尝试,但我发现只有当我override
使用build
方法时,它才能做到这一点。
以某种方式,当我在自定义override
,build
中使用ScaffoldState
的Widget
的MScaffold
方法时,它将阻止父窗口小部件使用相同类型的新Widget
进行自身重建。我还发现,当其中一个选项是Scaffold
,另一个选项是MScaffold
时,无论顺序如何,它的切换效果都很好。只有这两个选项均为MScaffold
时,它才不再切换。
[似乎每次MScaffoldState
每次都返回相同的build
结果。
这里是实现(如果将两个MScaffold
替换为Scaffold
,则可以使用:]
import 'package:flutter/material.dart';
import 'PageSwitcher.dart';
import 'MScaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'MScaffold switch error',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'MScaffold switch error'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
void _switchPage()
PageSwitcher().switchPage();
setState(() );
@override
Widget build(BuildContext context)
return PageSwitcher().pageNum == 1
? MScaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Page 1',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _switchPage,
tooltip: 'Switch',
child: Icon(Icons.refresh),
), // This trailing comma makes auto-formatting nicer for build methods.
)
: MScaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Page 2',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _switchPage,
tooltip: 'Switch',
child: Icon(Icons.refresh),
), // This trailing comma makes auto-formatting nicer for build methods.
);
这里是PageSwitcher
类,用于跟踪应用程序在哪个页面上:
class PageSwitcher
static final PageSwitcher _thisClass = PageSwitcher._internal();
PageSwitcher._internal();
factory PageSwitcher()
return _thisClass;
int pageNum = 1;
void switchPage()
pageNum = pageNum==1?2:1;
自定义Widget
,MScaffold
是Scaffold
的改动很小的版本,所以我不明白为什么会发生此错误。
这是MScaffold
的页面:
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class ShowSnackBar
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
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;
MScaffold(
Key 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),
super(key: key);
@override
ScaffoldState 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,
);
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;
MScaffoldState(
Key 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);
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);
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,
);
如您所见,MScaffold
接受与Scaffold
相同的参数,并使用这些参数生成Scaffold
。唯一不同的是使用ShowSnackBar
类来处理SnackBar
消息。除此之外,它只是使用Scaffold
参数,然后使用这些参数构建Scaffold
。
SOLUTION
解决方案是Saed Nabil的建议,加上MScaffold(Key key...
更改为MScaffold(this.key...
,MScaffoldState(Key key...
更改为MScaffoldState(this.key...
,因为它不接受密钥。
新演示文稿看起来像这样:
import 'package:flutter/material.dart';
import 'PageSwitcher.dart';
import 'MScaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'MScaffold switch error',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'MScaffold switch error'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
void _switchPage()
PageSwitcher().switchPage();
setState(() );
@override
Widget build(BuildContext context)
return PageSwitcher().pageNum == 1
? MScaffold(
key: ValueKey(1),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Page 1',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _switchPage,
tooltip: 'Switch',
child: Icon(Icons.refresh),
),
)
: MScaffold(
key:ValueKey(2),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Page 2',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _switchPage,
tooltip: 'Switch',
child: Icon(Icons.refresh),
),
);
新的MScaffold.dart
看起来像这样:
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class ShowSnackBar
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
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;
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),
super(key:key);
@override
ScaffoldState 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,
);
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;
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,
) : 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);
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,
);
而且有效!
这很麻烦,因为没有为您的MScaffold
类型的有状态小部件提供键
您可以在构建这样的支架对象时通过提供适当的键来解决此问题
MScaffold(
key: ValueKey('1'),
...
)
MScaffold(
key: ValueKey('2'),
...
)
以上是关于Flutter:为什么在构建函数中基于条件语句在脚手架之间进行切换,但不适用于自定义窗口小部件的主要内容,如果未能解决你的问题,请参考以下文章
让移动开发更轻松 闲鱼基于Flutter构建跨端APP应用实践
我如何在 FutureBuilder Widget 的构建器函数中等待 - Flutter
R语言使用party包中的cforest函数基于条件推理决策树(Conditional inference trees)构建随机森林使用varimp函数查看特征重要度使用table函数计算混淆矩阵