我对Flutter还是很陌生,并且在构建页面布局时正在寻找一些“最佳实践”建议。我来自Java背景,在这里我总是尽可能地重复使用,但是我不确定这真的是最好的方法。我有几个页面都将有一个应用栏,但它们都有自己的动作。这些页面中的每个页面将共享一个公用抽屉。最初,我开始着手创建通用根页面小部件,在其中选择抽屉中的项目时,通用页面的主体会发生变化,如下所示:
class HomePage extends StatefulWidget {
final BaseAuth auth;
final Function onSignedOut;
const HomePage({Key key, this.auth, this.onSignedOut}) : super(key: key);
@override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
final drawerItems = [
new DrawerItem("Home", Icons.home),
new DrawerItem("Pantry", Icons.store),
new DrawerItem("Barcode Scanner", Icons.scanner)
];
int _selectedDrawerIndex = 0;
bool _isEmailVerified;
_getDrawerItemWidget(int pos) {
switch (pos) {
case 0:
return new HomePageFragment();
case 1:
return new UserPantryFragment();
case 2:
return new BarcodeScannerFragment();
default:
return new Text("Error");
}
}
_onSelectItem(int index) {
setState(() => _selectedDrawerIndex = index);
Navigator.of(context).pop(); // close the drawer
}
@override
Widget build(BuildContext context) {
var drawerOptions = <Widget>[];
for (var i = 0; i < drawerItems.length; i++) {
var d = drawerItems[i];
drawerOptions.add(new ListTile(
leading: new Icon(d.icon),
title: new Text(d.title),
selected: i == _selectedDrawerIndex,
onTap: () => _onSelectItem(i),
));
}
AuthenticationContext authenticationContext =
AuthenticationContext.of(context);
return new FutureBuilder<FirebaseUser>(
future: authenticationContext.auth.getCurrentUser(),
initialData: null,
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> data) {
var name = data.data != null ? data.data.displayName : "";
var email = data.data != null ? data.data.email : " ";
var photoUrl = data.data != null && data.data.photoUrl != null
? data.data.photoUrl
: null;
return new Scaffold(
appBar: new AppBar(
title: new Text(drawerItems[_selectedDrawerIndex].title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
},
),
// overflow menu
PopupMenuButton<String>(
// onSelected: _signOut,
itemBuilder: (BuildContext context) {
return ['Logout'].map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
)
]),
drawer: new Drawer(
child: new Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text(name != null ? name : ""),
accountEmail: Text(email),
currentAccountPicture: CircleAvatar(
// backgroundImage: FadeInImage.memoryNetwork(
// placeholder: kTransparentImage,
// image: photoUrl != null ? photoUrl : "",
// ).image,
child: new Text(
photoUrl == null ? email[0].toUpperCase() : ""),
),
),
new Column(children: drawerOptions)
],
),
),
body: _getDrawerItemWidget(_selectedDrawerIndex));
});
}
但是,我现在想知道在每个屏幕上从头开始创建Scaffold而不是尝试使用共享的根页面是否会更好,因为我遇到了为每个页面轻松自定义AppBar的问题。最初我以为我可以在每个页面小部件上创建一些“ buildAppBar”函数并让根页面使用它,但这似乎不是一个容易实现的解决方案……至少不是以一种优雅的方式,我可以找到。
答案 0 :(得分:1)
您可以扩展StatelessWidget
以向类添加自定义参数,并在build方法中返回自定义的Scaffold
。类似于:
class MyScaffold extends StatelessWidget {
final Widget option1;
final Widget option2;
final Widget body;
const MyScaffold({
this.option1,
this.option2,
this.body,
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: option1,
drawer: option2,
body: body,
);
}
}
您还可以复制Scaffold
类中的其他属性,并将它们作为成员添加到MyScaffold
中(记住要像body
和options参数那样在构造函数中对其进行初始化)。
另一种将状态(读取:变量)传递到小部件树的选项是InheritedWidget
答案 1 :(得分:0)
为抽屉创建一个单独的小部件,并在需要的地方使用它。
管理抽屉状态class DrawerStateInfo with ChangeNotifier {
int _currentDrawer = 0;
int get getCurrentDrawer => _currentDrawer;
void setCurrentDrawer(int drawer) {
_currentDrawer = drawer;
notifyListeners();
}
void increment() {
notifyListeners();
}
}
将状态管理添加到小部件树
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
providers: <SingleChildCloneableWidget>[
ChangeNotifierProvider<DrawerStateInfo>(
builder: (_) => DrawerStateInfo()),
],
);
}
}
创建抽屉小部件以在应用程序中重用
class MyDrawer extends StatelessWidget {
MyDrawer(this.currentPage);
final String currentPage;
@override
Widget build(BuildContext context) {
var currentDrawer = Provider.of<DrawerStateInfo>(context).getCurrentDrawer;
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text(
"Home",
style: currentDrawer == 0
? TextStyle(fontWeight: FontWeight.bold)
: TextStyle(fontWeight: FontWeight.normal),
),
trailing: Icon(Icons.arrow_forward),
onTap: () {
Navigator.of(context).pop();
if (this.currentPage == "Home") return;
Provider.of<DrawerStateInfo>(context).setCurrentDrawer(0);
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) =>
MyHomePage(title: "Home")));
},
),
ListTile(
title: Text(
"About",
style: currentDrawer == 1
? TextStyle(fontWeight: FontWeight.bold)
: TextStyle(fontWeight: FontWeight.normal),
),
trailing: Icon(Icons.arrow_forward),
onTap: () {
Navigator.of(context).pop();
if (this.currentPage == "About") return;
Provider.of<DrawerStateInfo>(context).setCurrentDrawer(1);
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => MyAboutPage()));
},
),
],
),
);
}
}
在您的页面之一中使用抽屉
class MyAboutPage extends StatefulWidget {
@override
_MyAboutPageState createState() => _MyAboutPageState();
}
class _MyAboutPageState extends State<MyAboutPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('About Page'),
),
drawer: MyDrawer("About"),
);
}
}