简体   繁体   中英

How to change content of Stateless Widget

I'm working on weather forecast app (code same as from previous issue How to make SliverAppBar listen to botomSheet change ).

This model worked without errors for changing city, however, when I implemented BottomAppBar navigation (changing daily and hourly forecast), like below, it doesn't update the content anymore - function for getting city works, but CustomScrollView doesn't "redraw" for updated data.

I changed following:

Body of Scaffold-class app now loads from list, by index selected in BottomAppBar.

body: _children[_currentIndex]

List

final List<Widget> _children = [
    MyScrollView('hourly'),
    MyScrollView('daily')
  ];

And I put whole CustomScrollView into MyScrollView() class, alltogether with data fetching methods

class MyScrollView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new CustomScrollView(...)}
  }

How to change CustomScrollView after the city is changed?

Edit: After further research, my problem seems, that Stateless Widget cannot change its' State, but as soon as I change MyScrollView to Stateful, it isn't accepted as Scaffold's body.

Source Code

Make MyScrollView a Stateful Widget.

Edit:

Initialize _currentIndex to 0 and use setState to change it to 1 whenever the user click on the FAB.

If your trying to update the data from another class then check the below link:

How to Set/Update Sate of StatefulWidget from other StatefulWidget in Flutter?

Edit: 1

You're not returning any widget from your build method, so replace

@override
Widget build(BuildContext context) {
  new Scaffold(...);
}

with

@override
Widget build(BuildContext context) {
  return new Scaffold(...); // add return 
}

Edit: 2

Screenshot:

在此处输入图片说明

Full code:

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

Screenshot:

在此处输入图片说明


Not sure if it would help you.

Code:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM