gpt4 book ai didi

flutter - 导航不会使用自定义小部件更新 TabBarView 主体

转载 作者:IT王子 更新时间:2023-10-29 06:54:47 27 4
gpt4 key购买 nike

我想使用 TabBar 和 BottomNavigationBar 来控制相同的 TabBarView,其主体是自定义小部件 RankingTable 的列表(仅在演示中放置一个并使用文本小部件作为第二个子部件)。我的问题是点击导航栏时,它不会更新 TabBarView 主体,例如排名表不更新;在下面的演示代码中,我在表格中放置了相同的数据,但表格上方的日期下拉格式应该不同,因为我在点击导航栏时将不同的格式化程序传递到每个数据表中。也就是说,在第二张截图中,在 Nav1 上,它仍然显示与 Nav0 上的第一张截图相同的下拉日期格式。截图1: enter image description here

截图2: enter image description here

如果我在 TabBarView 主体中放置一个简单的小部件,例如 Text,那么当点击导航栏项目时它会按预期更新,不确定这是否意味着问题出在我的自定义 RankingTable 小部件。此外,尽管在导航栏中点击新项目时主体不会更新,但如果我切换选项卡,例如从 Tab1Tab2 并将其切换回 Tab1,然后主体得到正确更新,与相应的导航栏项目匹配。感觉当点击导航时 body 数据确实得到了更新,但它只是没有得到呈现。

import 'package:flutter/material.dart';

import 'package:intl/intl.dart';

void main() => runApp(new Demo());

class Demo extends StatefulWidget {
@override
_DemoState createState() => _DemoState();
}

class _DemoState extends State<Demo> with TickerProviderStateMixin {
int _currentIndex = 0;
Map<DateTime, List<RankingBase>> _playerDateRanking;
TabController controller;
List<_NavigationIconView> _navigationIconViews;
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
_navigationIconViews = <_NavigationIconView>[
_NavigationIconView(
icon: Icon(Icons.calendar_view_day),
title: 'Nav0',
color: Colors.deepOrange,
vsync: this,
),
_NavigationIconView(
icon: Icon(Icons.date_range),
title: 'Nav1',
color: Colors.deepOrange,
vsync: this,
),
];

_playerDateRanking = {
DateTime(2018, 9, 10): [
PlayerRanking('Tony', 7, 6, 140, 110, 80),
PlayerRanking('John', 7, 2, 120, 130, 56),
PlayerRanking('Mike', 8, 5, 120, 130, 70),
PlayerRanking('Clar', 6, 2, 100, 134, 63)
],
DateTime(2018, 9, 12): [
PlayerRanking('Tony', 7, 6, 140, 110, 80),
PlayerRanking('John', 7, 2, 120, 130, 56),
PlayerRanking('Mike', 8, 5, 120, 130, 70),
PlayerRanking('Clare', 6, 2, 100, 134, 63),
PlayerRanking('Jo', 5, 1, 100, 134, 63)
]
};
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: controller,
tabs: <Widget>[Text('Tab1'), Text('Tab2')],
),
),
body: TabBarView(
controller: controller, //TabController(length: 2, vsync: this),
children: <Widget>[
buildRankingTable(_currentIndex),
Text('TEst'),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
items: _navigationIconViews.map((x) => x.item).toList(),
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
),
),
);
}

Widget buildRankingTable(int currentIndex) {
if (currentIndex == 0) {
return RankingTable(_playerDateRanking, dateFormatter: 'yMMMEd');
} else if (currentIndex == 1) {
return RankingTable(_playerDateRanking,
dateFormatter: 'MMMM'); // different date formatter here!
}
return Text('TODO...');
}
}

class _NavigationIconView {
_NavigationIconView({
Widget icon,
//Widget activeIcon,
String title,
Color color,
TickerProvider vsync,
}) : _icon = icon,
_color = color,
_title = title,
item = new BottomNavigationBarItem(
icon: icon,
// activeIcon: activeIcon,
title: new Text(title),
backgroundColor: color,
),
controller = new AnimationController(
duration: kThemeAnimationDuration,
vsync: vsync,
) {
_animation = new CurvedAnimation(
parent: controller,
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
);
}
final Widget _icon;
final Color _color;
final String _title;
final BottomNavigationBarItem item;
final AnimationController controller;
CurvedAnimation _animation;
}

class PlayerRanking extends RankingBase {
String name;
PlayerRanking(this.name, played, won, pointsWon, pointsLost, duration)
: super(played, won, pointsWon, pointsLost, duration);
}

class RankingBase {
DateTime date;
int won;
int played;
int duration;
int pointsWon;
int pointsLost;
double get winRatio => won / played;
RankingBase(
this.played, this.won, this.pointsWon, this.pointsLost, this.duration);

static int performanceSort(RankingBase rb1, RankingBase rb2) {
if (rb1.winRatio > rb2.winRatio) return -1;
if (rb1.winRatio < rb2.winRatio) return 1;
if (rb1.played > rb2.played) return -1;
if (rb2.played == rb2.played) return rb1.pointsWon.compareTo(rb2.pointsWon);
return -1;
}
}

// this puts a scrollable datatable and optionally a header widget into a ListView
class RankingTable extends StatefulWidget {
final Map<DateTime, List<RankingBase>> rankingMap;
final bool hasHeaderWidget;
final String dateFormatter;
//final bool isPlayer;
RankingTable(this.rankingMap,
{this.dateFormatter, this.hasHeaderWidget = true});

@override
_RankingTableState createState() => _RankingTableState(this.rankingMap,
dateFormatter: this.dateFormatter, hasHeaderWidget: this.hasHeaderWidget);
}

class _RankingTableState extends State<RankingTable> {
Map<DateTime, List<RankingBase>> rankingMap;
final bool hasHeaderWidget;
final String dateFormatter;
//final bool isPlayer;
_RankingTableState(this.rankingMap,
{this.dateFormatter, this.hasHeaderWidget = true});

DateTime _selectedDate;

@override
initState() {
super.initState();
_selectedDate = rankingMap.keys.last;
}

DataTable buildRankingTable() {
rankingMap[_selectedDate].sort(RankingBase.performanceSort);
String nameOrPair =
rankingMap[_selectedDate].first is PlayerRanking ? 'Name' : 'Pair';
int rank = 1;

return DataTable(
columns: <DataColumn>[
DataColumn(label: Text('Rank')),
DataColumn(label: Text(nameOrPair)),
DataColumn(label: Text('Played')),
DataColumn(label: Text('Win Ratio')),
DataColumn(label: Text('Points Won-Loss')),
DataColumn(label: Text('Duration')),
],
rows: rankingMap[_selectedDate].map((RankingBase pr) {
DataCell titleCell;
if (pr is PlayerRanking)
titleCell = DataCell(Text('${pr.name}'));
else {
// var pair = pr as PairRanking;
// titleCell = DataCell(Text('${pair.player1Name}\n${pair.player2Name}'));
}
return DataRow(cells: [
DataCell(Text('${rank++}')),
titleCell,
DataCell(Text('${pr.played}')),
DataCell(Text('${NumberFormat("0.##%").format(pr.won / pr.played)}')),
DataCell(Text('${pr.pointsWon} - ${pr.pointsLost}')),
DataCell(Text('${pr.duration}')),
]);
}).toList(),
);
}

@override
Widget build(BuildContext context) {
List<Widget> childrenWidgets = [];
if (hasHeaderWidget) {
var dateDropdown = DropdownButton<DateTime>(
items: rankingMap.keys
.map((date) => DropdownMenuItem(
child: Text(
'${DateFormat(dateFormatter).format(date)}'), //yMMMEd
value: date,
))
.toList(),
value: _selectedDate,
onChanged: (value) {
setState(() {
_selectedDate = value;
});
},
);
childrenWidgets.add(dateDropdown);
}

childrenWidgets.add(SingleChildScrollView(
padding: EdgeInsets.all(20.0),
scrollDirection: Axis.horizontal,
child: buildRankingTable(),
));

return ListView(
padding: EdgeInsets.all(10.0),
children: childrenWidgets,
);
}
}

最佳答案

问题出在你的State上。

首先,您所在的州不需要构造函数。您可以通过调用 widget.youFinalField 访问小部件的变量。

问题是:当您返回一个 new RankingTable 时,底层的 state 不是重新创建而是重建(build didUpdateWidget 方法被调用)。因为您在构造中传递了变量(仅在第一次使用),所以您的格式化程序不会更新。

解决方案非常简单,而不是在您的 state 中使用构造函数,只需通过小部件访问变量即可。

工作代码状态代码:

    // this puts a scrollable datatable and optionally a header widget into a ListView
class RankingTable extends StatefulWidget {
final Map<DateTime, List<RankingBase>> rankingMap;
final bool hasHeaderWidget;
final String dateFormatter;
//final bool isPlayer;
RankingTable(this.rankingMap,
{this.dateFormatter, this.hasHeaderWidget = true});

@override
_RankingTableState createState() => _RankingTableState();
}

class _RankingTableState extends State<RankingTable> {


DateTime _selectedDate;


@override
void initState() {
super.initState();
_selectedDate = widget.rankingMap.keys.last;
}



DataTable buildRankingTable() {
widget.rankingMap[_selectedDate].sort(RankingBase.performanceSort);
String nameOrPair =
widget.rankingMap[_selectedDate].first is PlayerRanking ? 'Name' : 'Pair';
int rank = 1;

return DataTable(
columns: <DataColumn>[
DataColumn(label: Text('Rank')),
DataColumn(label: Text(nameOrPair)),
DataColumn(label: Text('Played')),
DataColumn(label: Text('Win Ratio')),
DataColumn(label: Text('Points Won-Loss')),
DataColumn(label: Text('Duration')),
],
rows: widget.rankingMap[_selectedDate].map((RankingBase pr) {
DataCell titleCell;
if (pr is PlayerRanking)
titleCell = DataCell(Text('${pr.name}'));
else {
// var pair = pr as PairRanking;
// titleCell = DataCell(Text('${pair.player1Name}\n${pair.player2Name}'));
}
return DataRow(cells: [
DataCell(Text('${rank++}')),
titleCell,
DataCell(Text('${pr.played}')),
DataCell(Text('')),
DataCell(Text('${pr.pointsWon} - ${pr.pointsLost}')),
DataCell(Text('${pr.duration}')),
]);
}).toList(),
);
}

@override
Widget build(BuildContext context) {
List<Widget> childrenWidgets = [];
if (widget.hasHeaderWidget) {
var dateDropdown = DropdownButton<DateTime>(
items: widget.rankingMap.keys
.map((date) => DropdownMenuItem(
child: Text(
'$date ${widget.dateFormatter}'), //yMMMEd
value: date,
))
.toList(),
value: _selectedDate,
onChanged: (value) {
setState(() {
_selectedDate = value;
});
},
);
childrenWidgets.add(dateDropdown);
}

childrenWidgets.add(SingleChildScrollView(
padding: EdgeInsets.all(20.0),
scrollDirection: Axis.horizontal,
child: buildRankingTable(),
));

return ListView(
padding: EdgeInsets.all(10.0),
children: childrenWidgets,
);
}
}

关于flutter - 导航不会使用自定义小部件更新 TabBarView 主体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52431855/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com