错误:在构建期间调用了 setState() 或 markNeedsBuild()

Posted

技术标签:

【中文标题】错误:在构建期间调用了 setState() 或 markNeedsBuild()【英文标题】:Error: setState() or markNeedsBuild() called during build 【发布时间】:2021-11-14 16:57:03 【问题描述】:

我目前正在构建一个天气应用程序,它可以为我们提供温度和气候信息(如果下雨、刮风或晴天)。我正在使用 http 和地理定位器包来构建我的应用程序。使用 api 密钥和 json解码我正在检索信息。

我收到以下错误

在 ws://127.0.0.1:57160/pspjoes2GlI=/ws 上侦听的调试服务

Error: setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
  Overlay-[LabeledGlobalKey<OverlayState>#044ff]
The widget which was currently being built when the offending call was made was:
  Builder
    at Object.throw_ [as throw] (http://localhost:64547/dart_sdk.js:5348:11)
    at http://localhost:64547/packages/flutter/src/widgets/widget_span.dart.lib.js:29299:23
    at framework.StatefulElement.new.markNeedsBuild (http://localhost:64547/packages/flutter/src/widgets/widget_span.dart.lib.js:29307:26)
    at overlay$.OverlayState.new.setState (http://localhost:64547/packages/flutter/src/widgets/widget_span.dart.lib.js:17689:43)
    at overlay$.OverlayState.new.rearrange (http://localhost:64547/packages/flutter/src/widgets/widget_span.dart.lib.js:31894:12)

这是我的代码

Main.dart

import 'package:flutter/material.dart';
import 'package:clima/screens/loading_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      theme: ThemeData.dark(),
      home: LoadingScreen(),
    );
  

LoadingScreen.dart

import 'package:flutter/material.dart';
import 'location_screen.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'dart:convert';
import'package:clima/services/weather.dart';
const APIkey = '9f2e463e61258ef5a8b9a9bd11733b62c';
class LoadingScreen extends StatefulWidget 
  @override
  _LoadingScreenState createState() => _LoadingScreenState();

double MyMarginAsDouble;
double latitude,longitude;
class _LoadingScreenState extends State<LoadingScreen> 
void initState()//if i call getlocation inside init method it automatically gets the location without the need of a button
  super.initState();
  getLocationData();

  Future<void> getLocationData()
  async 
 WeatherModel weatherModel = WeatherModel();
 var weatherData = weatherModel.getLocationWeather();
 Navigator.push(context,MaterialPageRoute(builder:(context)
   return LocationScreen(
     locationWeather: weatherData,//weather data links to networkHelper cls in networking.dart gets the weather info using http package
   );
 
 ));
  
@override
Widget build(BuildContext context) 
  return Scaffold(
    body:Center(
      child:SpinKitDoubleBounce(
        color:Colors.white,
        size:100,
      )
    )
  );


Location_screen.dart

import 'dart:convert';

import 'package:clima/services/weather.dart';
import 'package:geolocator/geolocator.dart';
import 'package:flutter/material.dart';
import 'package:clima/utilities/constants1.dart';
import 'package:clima/services/weather.dart';
WeatherModel o = WeatherModel();
class LocationScreen extends StatefulWidget 
  LocationScreen(this.locationWeather);//yow will be passed WeatherData variable (it contains the info of the weather retrieived frm the http api url) (WeatherData of the loading_Screen).
  final locationWeather;//LocationWeather receives the information
  @override
  _LocationScreenState createState() => _LocationScreenState();


class _LocationScreenState extends State<LocationScreen> 
  WeatherModel weather = WeatherModel();
  int temperature;
  double temp;
  String weatherIcon;
  int condition;
  String cityName;
  @override
  void initState()

    super.initState();
    updateUi(widget.locationWeather);


  void updateUi(dynamic weatherData)
  
   var condition = weatherData['weather'][0]['id'];//api contains id eg 300 for light drizzle and so on
    temp = weatherData['main']['temp'];
   temperature=temp.toInt();
   cityName = weatherData['name'];
weatherIcon =weather.getWeatherIcon(condition);//pass that condition to a method in getWeatherdata
   print(temperature);
  
  Future<void> getLocation()
  async 
    LocationPermission permission = await Geolocator.requestPermission();
    Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
    print(position);
  
  Widget build(BuildContext context) 
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage('images/location_background.jpg'),
            fit: BoxFit.cover,
            colorFilter: ColorFilter.mode(
                Colors.white.withOpacity(0.8), BlendMode.dstATop),
          ),
        ),
        constraints: BoxConstraints.expand(),
        child: SafeArea(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  FlatButton(
                    onPressed: () async 
                      var weatherData = await weather.getLocationWeather();//we need to gaurantee that this wont return null as it will take long time to execute
                      updateUi(weatherData);
                    ,
                    child: Icon(
                      Icons.near_me,
                      size: 50.0,
                    ),
                  ),
                  FlatButton(
                    onPressed: () ,
                    child: Icon(
                      Icons.location_city,
                      size: 50.0,
                    ),
                  ),
                ],
              ),
              Padding(
                padding: EdgeInsets.only(left: 15.0),
                child: Row(
                  children: <Widget>[
                    Text(
                      '$temperature°',
                      style: kTempTextStyle,
                    ),
                    Text(
                      weatherIcon ,
                      style: kConditionTextStyle,
                    ),
                  ],
                ),
              ),
              Padding(
                padding: EdgeInsets.only(right: 15.0),
                child: Text(
                  "$o.getMessage(temperature)in $cityName!",//
                  textAlign: TextAlign.right,
                  style: kMessageTextStyle,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  

Location.dart

导入'package:geolocator/geolocator.dart';

class location
  double latitude;
  double longitude;
  Future  getCurrentLocation()
  async 
    try
      LocationPermission permission = await Geolocator.requestPermission();
      Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low); //high is battery consuming
      LocationPermission permission1 = await Geolocator.checkPermission();
      latitude=position.latitude;
      longitude=position.longitude;
    
    catch(e)
    
      print(e);
    
  


Networking.dart

import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper 
  NetworkHelper(this.url);
  var url;
  Future getData() async 
    //var url = Uri.parse(url);
    //var url = Uri.parse('api.openweathermap.org/data/2.5/weather?q=London&appid=92e463e61258ef5a8b9a9bd11733b62c');
    http.Response response = await http.get(Uri.parse(url));
    if (response.statusCode == 200) 
      String data = response.body;
var decodedData = jsonDecode(data);
return decodedData;
    
    else 
      print(response.statusCode);
    
  

天气.dart

import '../location.dart';
import 'networking.dart';

class WeatherModel 

  Future getLocationWeather()
  async 
    location ob= new location();
    await ob.getCurrentLocation();
    print(ob.latitude);
    print(ob.longitude);
    //calling networkHelper
    NetworkHelper networkHelper = NetworkHelper('http://api.openweathermap.org/data/2.5/weather?lat=$ob.latitude&lon=$ob.longitude&appid=92e463e61258ef5a8b9a9bd11733b62c&units=metric');
    var WeatherData = await networkHelper.getData();
    return WeatherData;
  

  String getWeatherIcon(int condition) 
    if (condition < 300) 
      return '????';
     else if (condition < 400) 
      return '????';
     else if (condition < 600) 
      return '☔️';
     else if (condition < 700) 
      return '☃️';
     else if (condition < 800) 
      return '????';
     else if (condition == 800) 
      return '☀️';
     else if (condition <= 804) 
      return '☁️';
     else 
      return '????‍';
    
  

  String getMessage(int temp) 
    if (temp > 25) 
      return 'It\'s ???? time';
     else if (temp > 20) 
      return 'Time for shorts and ????';
     else if (temp < 10) 
      return 'You\'ll need ???? and ????';
     else 
      return 'Bring a ???? just in case';
    
  

city_Screen.dart

import 'package:flutter/material.dart';
import 'package:clima/utilities/constants1.dart';

class CityScreen extends StatefulWidget 
  @override
  _CityScreenState createState() => _CityScreenState();


class _CityScreenState extends State<CityScreen> 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage('images/city_background.jpg'),
            fit: BoxFit.cover,
          ),
        ),
        constraints: BoxConstraints.expand(),
        child: SafeArea(
          child: Column(
            children: <Widget>[
              Align(
                alignment: Alignment.topLeft,
                child: FlatButton(
                  onPressed: () ,
                  child: Icon(
                    Icons.arrow_back_ios,
                    size: 50.0,
                  ),
                ),
              ),
              Container(
                padding: EdgeInsets.all(20.0),
                child: null,
              ),
              FlatButton(
                onPressed: () ,
                child: Text(
                  'Get Weather',
                  style: kButtonTextStyle,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  

Conststants1.dart

import 'package:flutter/material.dart';

const kTempTextStyle = TextStyle(
  fontFamily: 'Spartan MB',
  fontSize: 100.0,
);

const kMessageTextStyle = TextStyle(
  fontFamily: 'Spartan MB',
  fontSize: 60.0,
);

const kButtonTextStyle = TextStyle(
  fontSize: 30.0,
  fontFamily: 'Spartan MB',
);

const kConditionTextStyle = TextStyle(
  fontSize: 100.0,
);

【问题讨论】:

您在 initState 中使用 Future,根据 Flutter 框架这是不正确的。在某处提取该方法,然后在 initState 中调用它 @Hamza 我已经在 init 语句之外编写了 getLocationData 方法,并在 init 状态下调用它。我没明白你想说什么。你能解释一下吗? 【参考方案1】:

像这样更改您的 getLocationData 方法:

getLocationData() async 
WeatherModel weatherModel = WeatherModel();
var weatherData = await weatherModel.getLocationWeather();
if (weatherData != null) 
  Navigator.push(context, MaterialPageRoute(builder: (context) 
    return LocationScreen(
      locationWeather: weatherData,
    );
  ));


【讨论】:

以上是关于错误:在构建期间调用了 setState() 或 markNeedsBuild()的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 错误 - 在构建期间调用了 setState() 或 markNeedsBuild()

Flutter:在构建期间调用 setState() 或 markNeedsBuild()

Flutter - 在构建期间调用 setState() 或 markNeedsBuild()

在构建期间调用 setState() 或 markNeedsBuild() - Flutter

(在构建时出现错误)在构建期间调用 setState() 或 markNeedsBuild()。相关的导致错误的小部件是 MaterialApp

在构建 CupertinoTabScaffold 期间调用 setState() 或 markNeedsBuild()