这些天我面临着 Dismissible 组件的一个相当令人惊讶的行为。使用窄屏幕时,我在其自己的页面中使用了相同的小部件 AirportWidget:
Scaffold(
appBar: AppBar(…),
body: RefreshIndicator(
key: _refreshIndicatorKey,
child: AirportWidget(…),
onRefresh: _refreshAirportList)
)
或者当在宽屏幕上显示时作为屏幕的右侧部分(右侧组件在一行中,组件列表占据左侧部分):
Row(children: <Widget>[
MyAirportsListWidget,
ViewState.isLargeScreen ? Expanded(child: Card(child:AirportWidget(…)) : Container()])
现在我的问题是:我的 AirportWidget,在这两种情况下都是相同的 Widget,包含一列元素,每个元素都嵌入到 Dismissible 中。当 AirportWidget 显示在自己的页面上时,这一切都可以正常工作,但是当显示在宽屏幕的右侧时,它不会......它仍然以某种方式识别 Dismissible 元素何时向正确的方向滑动(当什么都不做在另一个方向滑动),但不是显示它在那个方向消失然后调用 onDismissed 方法,而是显示没有动画,只是跳到列的顶部(我猜是重建组件)。
从这里给某人敲钟,还是我需要提供完整的细节?
此致
更多代码细节:这是一个有点复杂的设计。包含 Dismissible 的 AirportWidget 本身由两个选项卡组成:第一个选项卡显示机场天气(现在与我们无关,因为它不包含 Dismissible),第二个选项卡显示 NOTAM(机场信息)。那是包含 Dismissible 元素的那个。
class AirportWidget extends StatefulWidget {
Airport _airport;
int _currentIndex;
AirportWidget(this._airport, {int index, Key key}) : super(key: key) {
_currentIndex = index != null? index : ViewState.airportSelectedTabIndex;
}
@override
AirportWidgetState createState() => AirportWidgetState();
}
class AirportWidgetState extends State<AirportWidget> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
// Bottom tabs and active tab index
List<Widget> _children;
@override
void initState() {
super.initState();
_children = [
AirportWeatherWidget(widget._airport),
AirportNotamsWidget(widget._airport),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: widget._airport == null? noAirportScreen(context) : _children[widget._currentIndex],
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: themes.AppColors.CREAM_FONT,
onTap: onTabTapped, // new
currentIndex: widget._currentIndex, // new
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.filter_drama),
title: new Text('WEATHER'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.library_books),
title: new Text("NOTAMS"),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
ViewState.airportSelectedTabIndex = index;
widget._currentIndex = index;
});
}
}
现在让我们看看包含 Dismissible 元素的 AirportNotamsWidget:
class AirportNotamsWidget extends StatefulWidget {
Airport _airport;
AirportNotamsWidget(this._airport);
@override
AirportNotamsWidgetState createState() => AirportNotamsWidgetState();
}
class AirportNotamsWidgetState extends State<AirportNotamsWidget> {
// final _scaffoldKey = GlobalKey<ScaffoldState>(); // used for snackbar display
@override
Widget build(BuildContext context) {
List<Widget> notamsWidgetsList = List<Widget>();
Color nonSelectedStarColor = Theme.of(context).brightness == Brightness.light?
themes.AppColors.LIGHT_GREY : themes.AppColors.CREAM_FONT;
Color selectedStarColor = Theme.of(context).brightness == Brightness.light?
themes.AppColors.YELLOW : themes.AppColors.DARK_YELLOW;
Widget topRow = Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(child: Text(widget._airport.name, style: Theme.of(context).textTheme.subtitle)),
Row( children: [
IconButton(icon: Icon(Icons.ac_unit, color: ViewState.dislplaySnowtams? Theme.of(context).accentColor :
Theme.of(context).unselectedWidgetColor),
onPressed: () {
setState(() {
ViewState.dislplaySnowtams = !ViewState.dislplaySnowtams;
});
}
),
MaterialButton(child: Text(ViewState.displayRelevantNotamsOnly? "RLV" : "ALL"), minWidth: 40,
onPressed: () {
setState(() {
ViewState.displayRelevantNotamsOnly = !ViewState.displayRelevantNotamsOnly;
});
}
)
])
],);
notamsWidgetsList.add(topRow);
// Text("NOTAMs view not yet implemented", style: Theme.of(context).textTheme.body1),
notamsWidgetsList.add(Container(
color: Theme.of(context).selectedRowColor,
child: Row(children: [Text("NOTAMS", style: TextStyle(color:
Theme.of(context).brightness == Brightness.light? themes.AppColors.LIGHT_GREY: themes.AppColors.CREAM_FONT,
fontWeight: FontWeight.bold),)],
mainAxisAlignment: MainAxisAlignment.center,),
padding: EdgeInsets.only(left:0, top: 3, bottom: 3), margin: EdgeInsets.only(top: 15, bottom: 6),
));
if(widget._airport.normalNotamsList.isNotEmpty) {
for(int i = 0; i < widget._airport.normalNotamsList.length; i++) {
NOTAM notam = widget._airport.normalNotamsList[i];
notamsWidgetsList.add(
Dismissible(
// unique key in order to be able to remove the item from a sublist and add it to another
key: Key(notam.key + DateTime.now().toString()),
background: Container(
alignment: AlignmentDirectional.centerEnd,
child: Padding(
padding: EdgeInsets.fromLTRB(themes.DISMISSIBLE_TILES_BACKGROUND_LATERAL_PADDING, 0.0,
themes.DISMISSIBLE_TILES_BACKGROUND_LATERAL_PADDING, 0.0),
child: Column(children: [Text("NON"), Text("RELEVANT")], mainAxisAlignment: MainAxisAlignment.center,)
),
),
child: Card(
child: Container(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(notam.id + " - " + notam.Subject + " " + notam.Modifier,
style: Theme.of(context).textTheme.subhead)),
IconButton(icon: Icon(Icons.star, color: notam.isStarred()? selectedStarColor : nonSelectedStarColor),
onPressed: ()async {
setState(() {
widget._airport.toggleNotamStarred(notam);
PersistenceManager.updateNotam(notam);
});
}),
]),
Text(ViewUtil.printNotamMessage(notam.message), style: Theme.of(context).textTheme.body1),
Row(children:
[Text("From: " + ViewUtil.printDate(context, notam.startDate) + " " + ViewUtil.printTime(context, notam.startDate)
+ " To: " + ViewUtil.printDate(context, notam.endDate) + " " + ViewUtil.printTime(context, notam.endDate),
style: Theme.of(context).textTheme.body2,)], mainAxisAlignment: MainAxisAlignment.end,),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
padding: EdgeInsets.all(10),
),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
print("DISMISSED");
setState(() {
widget._airport.normalNotamsList.remove(notam);
widget._airport.nonRelevantNotamsList.add(notam);
notam.priority = NOTAM.PRIORITY_IRRELEVANT;
Util.sortNotamList(widget._airport.nonRelevantNotamsList);
PersistenceManager.updateNotam(notam);
});
}
),
);
}
notamsWidgetsList.add(Divider());
}
if(!ViewState.displayRelevantNotamsOnly) {
if(widget._airport.nonRelevantNotamsList.isNotEmpty) {
notamsWidgetsList.add(Text("NON OPERATION RELEVANT NOTAMs"));
widget._airport.nonRelevantNotamsList.forEach((notam) {
notamsWidgetsList.add(
Dismissible(
// unique key in order to be able to remove the item from a sublist and add it to another
key: Key(notam.key + DateTime.now().toString()),
background: Container(
alignment: AlignmentDirectional.centerStart,
child: Padding(
padding: EdgeInsets.fromLTRB(themes.DISMISSIBLE_TILES_BACKGROUND_LATERAL_PADDING, 0.0,
themes.DISMISSIBLE_TILES_BACKGROUND_LATERAL_PADDING, 0.0),
child: Text("RELEVANT"),
),
),
child: Card(
child: Container(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(notam.id + " - " + notam.Subject + " " + notam.Modifier,
style: Theme.of(context).textTheme.subhead)),
IconButton(icon: Icon(Icons.star, color: notam.isStarred()? selectedStarColor : nonSelectedStarColor),
onPressed: ()async {
setState(() {
widget._airport.toggleNotamStarred(notam);
PersistenceManager.updateNotam(notam);
});
}),
]),
Text(ViewUtil.printNotamMessage(notam.message), style: Theme.of(context).textTheme.body1),
Row(children:
[Text("From: " + ViewUtil.printDate(context, notam.startDate) + " " + ViewUtil.printTime(context, notam.startDate)
+ " To: " + ViewUtil.printDate(context, notam.endDate) + " " + ViewUtil.printTime(context, notam.endDate),
style: Theme.of(context).textTheme.body2,)], mainAxisAlignment: MainAxisAlignment.end,),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
padding: EdgeInsets.all(10),
)),
direction: DismissDirection.startToEnd,
onDismissed: (direction) {
if (direction == DismissDirection.startToEnd) {
setState(() {
widget._airport.nonRelevantNotamsList.remove(notam);
widget._airport.normalNotamsList.add(notam);
notam.priority = NOTAM.PRIORITY_NORMAL;
Util.sortNotamList(widget._airport.normalNotamsList);
PersistenceManager.updateNotam(notam);
});
}
}
),
);
});
}
}
return Builder(
builder: (context) =>
SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: notamsWidgetsList,
)
)
)
);
}
}
这是应用程序在“宽屏”模式下的截图:当我们在左侧列表中选择一个机场时,机场的两个选项卡的详细信息显示在屏幕的右半部分。在这里,我们可以看到 NOTAM 选项卡,其中包含有问题的 Dismissible 卡。
如下图在窄屏模式下显示时,相同的 Dismissible 卡可以正常工作