如何更改无状态小组件的内容

时间:2019-08-29 15:22:00

标签: flutter flutter-layout

我正在开发天气预报应用程序(与上一期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的主体。

Source Code

Screenshot of what I'm doing

3 个答案:

答案 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 
}

编辑:2

屏幕截图:

enter image description here

完整代码:

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)

屏幕截图:

enter image description here


不确定是否会帮助您。

代码:

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),
          ],
        ),
      ),
    );
  }
}