Flutter开发:如何在Dialog对话框中展示获取到的天气数据
Flutter定位天气Dialog展示问题修复
现存问题
- 异步时序错误:
queryWeather为异步方法,点击按钮后直接同步弹出Dialog,此时定位、天气接口请求尚未完成,Dialog弹出后不属于当前页面Widget树,不会响应页面State的更新,因此永远停留在加载状态无法展示天气数据 - 缺少权限校验:使用geolocator获取定位前需要校验位置权限是否授予,缺少权限判断会导致定位失败
- 无异常兜底逻辑:定位请求、天气接口请求均存在失败可能,未做异常捕获会触发未处理异常崩溃
修复方案
将Dialog弹出逻辑整合到异步方法内部,请求前先弹出加载态Dialog,拿到数据后关闭加载Dialog再弹出天气展示Dialog,同时补充权限校验和异常捕获逻辑,修复后完整代码如下:
import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:weather/weather.dart'; enum AppState { NOT_DOWNLOADED, DOWNLOADING, FINISHED_DOWNLOADING } class MyAppp extends StatefulWidget { @override _MyApppState createState() => _MyApppState(); } class _MyApppState extends State<MyAppp> { String key = '856822fd8e22db5e1ba48c0e7d69844a'; late WeatherFactory ws; List<Weather> _data = []; AppState _state = AppState.NOT_DOWNLOADED; @override void initState() { super.initState(); ws = WeatherFactory(key); } Future<void> queryWeather() async { FocusScope.of(context).requestFocus(FocusNode()); // 先弹出加载Dialog showDialog( context: context, barrierDismissible: false, builder: (context) => const Center(child: CircularProgressIndicator()), ); try { // 权限校验 LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { throw "位置权限被拒绝,无法获取定位"; } } if (permission == LocationPermission.deniedForever) { throw "位置权限被永久拒绝,请前往设置开启权限"; } // 获取定位 var position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); var lat = position.latitude; var long = position.longitude; // 请求天气数据 Weather weather = await ws.currentWeatherByLocation(lat, long); _data = [weather]; _state = AppState.FINISHED_DOWNLOADING; // 关闭加载Dialog if (mounted) Navigator.pop(context); // 弹出展示天气的Dialog if (mounted) { showDialog<String>( context: context, builder: (BuildContext context) { return Center( child: Container( height: 500, width: 320, color: Colors.white, child: ListView.separated( itemCount: _data.length, itemBuilder: (context, index) { return ListTile( title: Text("温度:${_data[index].temperature?.celsius?.toStringAsFixed(1)}℃\n" "天气:${_data[index].weatherMain}\n" "城市:${_data[index].areaName}"), ); }, separatorBuilder: (context, index) => const Divider(), ), ), ); }, ); } } catch (e) { // 关闭加载Dialog if (mounted) Navigator.pop(context); // 弹出错误提示 if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("请求失败:$e")), ); } } } Widget _buttons() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container( margin: const EdgeInsets.all(5), child: TextButton( onPressed: queryWeather, style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.blue)), child: const Text( 'Fetch weather', style: TextStyle(color: Colors.white), ), ), ), ], ); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Weather Example App'), ), body: Column( children: <Widget>[ _buttons(), const Text( 'Output:', style: TextStyle(fontSize: 20), ), const Divider( height: 20.0, thickness: 2.0, ), ], ), ), ); } }
额外配置注意
- Android端需要在
android/app/src/main/AndroidManifest.xml中添加位置权限声明 - iOS端需要在
ios/Runner/Info.plist中添加位置权限使用描述
内容的提问来源于stack exchange,提问作者Deepak
相关产品推荐
相关产品推荐

