我正在开发天气预报应用程序(与上一期How to make SliverAppBar listen to botomSheet change的代码相同)。
此模型在更改城市时没有错误,但是,当我实现BottomAppBar导航(更改每日和每小时预测)时,如下所示,它不再更新内容-用于获取城市作品的功能,但CustomScrollView却没有“重绘”以获取更新的数据。
我更改了以下内容:
脚手架类应用的主体现在从列表中加载,通过在BottomAppBar中选择的索引进行。
body: _children[_currentIndex]
列表
final List<Widget> _children = [
MyScrollView('hourly'),
MyScrollView('daily')
];
然后将整个CustomScrollView和数据获取方法一起放入MyScrollView()类
class MyScrollView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new CustomScrollView(...)}
}
更改城市后如何更改 CustomScrollView ?
编辑: 经过进一步的研究,我的问题似乎是,无状态小部件无法更改其状态,但是一旦我将 MyScrollView 更改为有状态,便不会被接受为Scaffold的主体。
答案 0 :(得分:1)
使MyScrollView
为有状态的小部件。
编辑:
每当用户单击FAB时,将_currentIndex初始化为0并使用setState将其更改为1。
如果您尝试从另一个类更新数据,请检查以下链接:
How to Set/Update Sate of StatefulWidget from other StatefulWidget in Flutter?
答案 1 :(得分:0)
修改:1
您不会通过build
方法返回任何小部件,因此请替换
@override
Widget build(BuildContext context) {
new Scaffold(...);
}
与
@override
Widget build(BuildContext context) {
return new Scaffold(...); // add return
}
屏幕截图:
完整代码:
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _currentIndex = 0;
String _cityGet;
double _cityLat;
double _cityLon;
@override
void initState() {
super.initState();
// initializeDateFormatting();
}
Widget _getChild(int index) {
switch (index) {
case 0:
return MyScrollView("hourly");
case 1:
return MyScrollView("daily");
}
return MyScrollView("hourly");
}
Future _changeCity(BuildContext context) async {
Map results = await Navigator.of(context).push(MaterialPageRoute<Map>(builder: (BuildContext context) {
return CityPage();
}));
if (results != null && results.containsKey('enter')) {
_cityGet = '${results['enter']}, ${results['code']}';
_cityLat = results['lat'];
_cityLon = results['lon'];
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
//todo: change colors
//todo: set theme styles for text
//todo: maybe delete appbar?
floatingActionButton: FloatingActionButton(
child: Icon(Icons.location_city),
backgroundColor: Theme.of(context).accentColor,
onPressed: () => _MyScrollViewState()._changeCity(context),
),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 5,
clipBehavior: Clip.antiAlias,
child: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
backgroundColor: Colors.grey.shade900,
selectedItemColor: Colors.yellow.shade600,
items: [
BottomNavigationBarItem(icon: Icon(Icons.watch_later), title: Text('Hodinová predpoveď')),
BottomNavigationBarItem(icon: Icon(Icons.calendar_today), title: Text('Denná predpoveď')),
]),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
body: _getChild(_currentIndex));
//backgroundColor: Colors.grey.shade700);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
class MyScrollView extends StatefulWidget {
final String rate;
const MyScrollView(this.rate);
@override
_MyScrollViewState createState() => _MyScrollViewState();
}
class _MyScrollViewState extends State<MyScrollView> {
String interval;
String _cityName;
var _cityLat;
var _cityLon;
// MyScrollView(this.interval);
String _rate; // do whatever you want to do with this
// you are free to call setState anywhere from this class
@override
void initState() {
super.initState();
_rate = widget.rate;
}
Future _changeCity(BuildContext context) async {
Map results = await Navigator.of(context).push(MaterialPageRoute<Map>(builder: (BuildContext context) {
return CityPage();
}));
if (results != null && results.containsKey('enter')) {
_cityName = '${results['enter']}, ${results['code']}';
_cityLat = results['lat'];
_cityLon = results['lon'];
}
}
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
centerTitle: true,
title: Text(
_cityName == null ? 'Liptovský Mikuláš, SK' : _cityName,
style: TextStyle(fontSize: 17.5, fontWeight: FontWeight.w200),
),
expandedHeight: 300,
floating: true,
pinned: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
background: Container(
child: updateCurrentWeather(),
)),
),
FutureBuilder(
future: _cityLat == null || _cityLon == null ? getWeather(49.083351, 19.609819) : getWeather(_cityLat, _cityLon),
builder: (BuildContext context, AsyncSnapshot<Map> snapshot) {
var forecast = snapshot.data;
var childrenCount = 0;
if (snapshot.connectionState != ConnectionState.done || snapshot.hasData == null)
childrenCount = 1;
else if (interval == 'hourly') {
childrenCount = 24;
} else {
childrenCount = 8;
}
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
if (snapshot.connectionState != ConnectionState.done) {
//todo handle state
return Container(); //todo set progress bar
}
if (interval == null || forecast == null) {
return Container();
}
var tempMax = forecast['$interval']['data'][index]['temperatureMax'];
var tempMin = forecast['$interval']['data'][index]['temperatureMin'];
var temperature = forecast['$interval']['data'][index]['temperature'];
String icon = forecast['$interval']['data'][index]['icon'];
var template = DateFormat('Hm');
int hour = forecast['$interval']['data'][index]['time'];
var hourObject = DateTime.fromMillisecondsSinceEpoch(hour * 1000);
String time = template.format(hourObject);
// initializeDateFormatting('sk');
var templateDay = DateFormat('EEEE', 'sk');
int dayData = forecast['$interval']['data'][index]['time'];
var dayObject = DateTime.fromMillisecondsSinceEpoch(dayData * 1000);
String capitalize(String s) => s[0].toUpperCase() + s.substring(1);
String dayUncap = templateDay.format(dayObject);
String day = capitalize(dayUncap);
String summary = forecast['$interval']['data'][index]['summary'];
var probability = forecast['$interval']['data'][index]['precipProbability'];
var precipitation = forecast['$interval']['data'][index]['precipIntensity'];
var humidity = forecast['$interval']['data'][index]['humidity'];
var uv = forecast['$interval']['data'][index]['uvIndex'];
var pressure = forecast['$interval']['data'][index]['pressure'];
return Card(
margin: index == 0 ? EdgeInsets.fromLTRB(20, 6, 20, 3) : EdgeInsets.fromLTRB(20, 3, 20, 3),
color: Colors.black12,
child: ExpansionTile(
leading: Image.asset(chocolateImage),
trailing: Text(
interval == 'hourly' ? '${temperature.toStringAsFixed(0)}°' : '${tempMin.toStringAsFixed(0)}° ${tempMax.toStringAsFixed(0)}°',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 20.0),
),
title: RichText(
text: TextSpan(
text: interval == 'hourly' ? '$time ' : index == 0 ? 'Dnes ' : index == 1 ? 'Zajtra ' : '$day ',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
children: <TextSpan>[TextSpan(text: '$summary', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 15.5))])),
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
//height: 80,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
// TEMP STATS - HOUR
padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.thermometer,
// size: 20,
// ),
),
Text(
interval == 'hourly' ? '${temperature.toStringAsFixed(0)}°' : '',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('Teplota', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// RAIN STATS - HOUR
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.umbrella,
// size: 20,
// ),
),
Text(
'${(probability * 100).toStringAsFixed(0)}%',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('${precipitation.toStringAsFixed(2)} mm', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// HUMIDITY STATS - HOUR
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.humidity,
// size: 20,
// ),
),
Text(
'${(humidity * 100).toStringAsFixed(0)}%',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('Vlhkosť', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// UV STATS - HOUR
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.day_sunny,
// size: 20,
// ),
),
Text(
'UV $uv',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('Žiarenie', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// PRESSURE STATS - HOUR
padding: const EdgeInsets.fromLTRB(0, 0, 10, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.barometer,
// size: 20,
// ),
),
Text(
'${pressure.toStringAsFixed(0)} hpa',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('Tlak', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
],
),
)
],
),
);
}, childCount: childrenCount),
);
},
)
],
);
}
Widget updateCurrentWeather() {
return FutureBuilder(
future: getWeather(49.083351, 19.609819),
builder: (BuildContext context, AsyncSnapshot<Map> snapshot) {
if (snapshot.hasData) {
Map content = snapshot.data;
var temperature = content['currently']['temperature'];
var probability = content['currently']['precipProbability'];
var precipitation = content['currently']['precipIntensity'];
var windSpeed = content['currently']['windSpeed'];
int windBearing = content['currently']['windBearing'];
var moonPhase = content['daily']['data'][0]['moonPhase'];
var humidity = content['currently']['humidity'];
String icon = content['currently']['icon'];
//var template = DateFormat('Hm','sk');
// var timeFormat = DateFormat.Hm('sk');
// int sunrise = content['daily']['data'][0]['sunriseTime'];
// var sunriseObject = DateTime.fromMillisecondsSinceEpoch(sunrise*1000);
// String sunriseTime = timeFormat.format(sunriseObject);
// int sunset = content['daily']['data'][0]['sunsetTime'];
// var sunsetObject = DateTime.fromMillisecondsSinceEpoch(sunset);
// String sunsetTime = timeFormat.format(sunsetObject);
String direction;
String phase;
var windIcon;
var moonIcon;
if (windSpeed != 0) {
if ((0 <= windBearing && windBearing <= 22.5) || (360 > windBearing && 337.5 < windBearing)) {
direction = 'S';
// windIcon = WeatherIcons.wind_deg_180;
} else if (22.5 < windBearing && windBearing <= 67.5) {
direction = 'SV';
// windIcon = WeatherIcons.wind_deg_225;
} else if (67.5 < windBearing && windBearing <= 112.5) {
direction = 'V';
// windIcon = WeatherIcons.wind_deg_270;
} else if (112.5 < windBearing && windBearing <= 157.5) {
direction = 'JV';
// windIcon = WeatherIcons.wind_deg_315;
} else if (157.5 < windBearing && windBearing <= 202.5) {
direction = 'J';
// windIcon = WeatherIcons.wind_deg_0;
} else if (202.5 < windBearing && windBearing <= 247.5) {
direction = 'JZ';
// windIcon = WeatherIcons.wind_deg_45;
} else if (247.5 < windBearing && windBearing <= 292.5) {
direction = 'Z';
// windIcon = WeatherIcons.wind_deg_90;
} else if (292.5 < windBearing && windBearing <= 337.5) {
direction = 'SZ';
// windIcon = WeatherIcons.wind_deg_135;
}
} else {
direction = '';
// windIcon = WeatherIcons.na;
}
if (moonPhase == 0) {
// moonIcon = WeatherIcons.moon_new;
phase = 'Nov';
} else if (0 < moonPhase && moonPhase < 0.25) {
// moonIcon = WeatherIcons.moon_alt_waxing_crescent_3;
phase = 'Dorastá';
} else if (moonPhase == 0.25) {
// moonIcon = WeatherIcons.moon_first_quarter;
phase = '1. štvrť';
} else if (0.25 < moonPhase && moonPhase < 0.5) {
// moonIcon = WeatherIcons.moon_alt_waxing_gibbous_3;
phase = 'Dorastá';
} else if (moonPhase == 0.5) {
// moonIcon = WeatherIcons.moon_full;
phase = 'Spln';
} else if (0.5 < moonPhase && moonPhase < 0.75) {
// moonIcon = WeatherIcons.moon_alt_waning_gibbous_3;
phase = 'Cúva';
} else if (moonPhase == 0.75) {
// moonIcon = WeatherIcons.moon_third_quarter;
phase = '3. štvrť';
} else {
// moonIcon = WeatherIcons.moon_waning_crescent_3;
phase = 'Cúva';
}
return Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
Stack(
alignment: Alignment.topCenter,
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(25, 30, 140, 0),
child: Image.asset(
chocolateImage,
width: 160,
)),
Container(padding: const EdgeInsets.fromLTRB(0, 50, 0, 0), child: Image.asset(chocolateImage)),
],
),
Padding(padding: const EdgeInsets.fromLTRB(0, 10, 0, 0)),
Row(
//crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
// TEMP STATS - CURRENT
padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.thermometer,
// size: 20,
// ),
),
Text(
'${temperature.toStringAsFixed(0)}°',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('Teplota', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// RAIN STATS - CURRENT
//padding: const EdgeInsets.fromLTRB(0, 220, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.umbrella,
// size: 20,
// ),
),
Text(
'${(probability * 100).toStringAsFixed(0)}%',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('${precipitation.toStringAsFixed(2)} mm', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// HUMIDITY STATS - CURRENT
//padding: const EdgeInsets.fromLTRB(0, 220, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
// child: Icon(
// WeatherIcons.humidity,
// size: 20,
// ),
),
Text(
'${(humidity * 100).toStringAsFixed(0)}%',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
Text('Vlhkosť', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// WIND STATS - CURRENT
//padding: const EdgeInsets.fromLTRB(0, 220, 0, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
child: Icon(
windIcon,
size: 20,
)),
Text(
'${windSpeed.toStringAsFixed(1)} m/s',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
//todo: condition update - if wind speed == 0: wind bearing= none
Text('$direction', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container(
// MOON STATS - CURRENT
padding: const EdgeInsets.fromLTRB(0, 0, 20, 0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 6),
child: Icon(
moonIcon,
size: 20,
)),
Text(
'$phase',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
),
//todo: condition update - if wind speed == 0: wind bearing= none
Text('Fáza', style: TextStyle(fontWeight: FontWeight.w300, fontSize: 14.0))
],
)),
Container()
],
)
],
),
);
} else {
return Container();
}
});
}
Future<Map> getWeather(double lat, double lon) async {
String key = '847155bb7e53129f8c2d68472a0b07b6';
//todo: switch to deploy key
String apiUrl = 'https://api.darksky.net/forecast/$key/$lat,$lon?lang=sk&units=si';
http.Response response = await http.get(apiUrl);
return json.decode(response.body);
}
}
答案 2 :(得分:0)
屏幕截图:
不确定是否会帮助您。
代码:
void main() => runApp(MaterialApp(home: MyPage()));
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
int _count = 0;
String _text = "";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Weather"),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => setState(() => ++_count),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.call),
onPressed: () => setState(() => _text = "Call"),
),
IconButton(
icon: Icon(Icons.message),
onPressed: () => setState(() => _text = "Message"),
)
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Count: $_count"),
SizedBox(height: 8),
Text(_text),
],
),
),
);
}
}