共计 6553 个字符,预计需要花费 17 分钟才能阅读完成。
经过前面的对于Flutter的介绍,我们现在已经可以开始写我们的天气预报APP的界面了。
项目Github地址:a1203991686/CoolWeather_Flutter
1. 大致界面
最终写成的大致界面如图:

我们可以把这个界面拆分成如下部分:

可以看到我们APP主要有最上面用来显示地点和刷新时间的Title、显示温度和天气的两个Text、显示3天预报的ListView、显示空气质量的GridView、以及最后显示生活建议的ListView。
此处会用到我们前面学过的所有知识,如果有同学没有看过前面内容的,可以看下本系列前面的文章。
2. 创建项目
首先我们创建一个新的Flutter项目:
- 在AndroidStudio点击
File->New Flutter Project;
- 接着选择
Flutter Application->NEXT。接着填写你的项目名称等一系列信息后点击next;
- 接着输入你的组织/公司名称作为包名,最后点击
Finish即可,这样就新建了一个Flutter项目,并且Flutter会自动为你生成一个计数器Demo;

- 让我们把
mian.dart里面的所有注释以及_MyHomePageState里面的build方法里面的代码都删掉;
- 接着在
lib文件夹下新建一个文件夹,名叫view,到时候我们把所有与界面有关的代码文件都放在这个文件夹下面;
- 然后我们在
view文件夹下面新建一个dart文件,命名为main_page。这个就是我们的天气详情页。
3. 设计好天气详情页框架
首先我们需要导入material包,我们项目主要用material风格UI来写。
在开头输入import 'package:flutter/material.dart';,这样就导入了material包。接着创建我们的界面类MainPage。
由于我们在到时候写好网络请求后,所有的数据都得从网络获取,并且使用了异步的方法,也就是说我们先把页面加载了然后等获取的数据回来了在通知Flutter更新状态,所以这块我们得使用StatefulWidget。
1 2 3
4 5 6 7 8 9 10 11 12
13
|
class MainPage extends StatefulWidget { MainPage({Key key}) : super(key: key);
@override MainPageState createState() => new MainPageState(); }
class MainPageState extends State<MainPage> { @override Widget build(BuildContext context) { } }
|
接着我们就得开始写build()里面的内容了。由于我们需要一个全屏的背景图片,所以我们就使用Container作为我们最外层的Widget,并使用BoxDecoration装饰容器配合DecorationImage来放置图片:
1 2 3
4 5 6 7 8 9 10 11 12
|
@override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage("http://blog.mrabit.com/bing/today"), fit: BoxFit.cover, ), ), child: _weatherBody(), ); }
|
这个时候我们的运行结果如图(①你背景图片多半和我不一样,因为上面那个Uri获取的是必应的每日一图,每天图片都不一样;②运行的时候记得把child: _weatherBody(),给注释掉,因为我们还没有定义_weatherBody()这个方法):

4. 设计title
为了方便我就直接把剩下的布局单领出来放到_weatherBody()这个方法:
1 2 3
|
Widget _weatherBody() { return }
|
由于我们需要实现一个有Title的页面,所以最外层我选用了Scaffold:
1 2 3
4 5 6 7 8 9 10 11 12
13
14 15 16 17 18 19 20 21 22 23 24 25
|
Widget _weatherBody() { return Scaffold( backgroundColor: Colors.transparent, appBar: AppBar( centerTitle: true, title: Text( "北京", style: TextStyle(fontSize: 25.0), ), backgroundColor: Colors.transparent, actions: <Widget>[ Container( alignment: Alignment.center, child: Text( "12:10", textAlign: TextAlign.center, ), ) ], ), body: SingleChildScrollView( ), ); }
|
这个时候的运行结果是:

5. 设计下面天气预报页面
接下来可以开始下下面的天气显示页面了。
当前温度和天气情况
首先写一个纵向线性布局Column:
1 2 3
4 5 6 7
|
body: SingleChildScrollView( child: Column( children: <Widget>[ ] ) ),
|
由于我们要显示在屏幕最右边,所以使用Align:
1 2 3
4 5 6 7 8 9 10 11 12
13
14 15 16 17 18 19 20 21 22 23 24 25 26
|
body: SingleChildScrollView( child: Column( children: <Widget>[ Align( alignment: Alignment.centerRight, child:Text( "26°C", style: TextStyle( fontSize: 50.0, color: Colors.white, ), ), ), Align( alignment: Alignment.centerRight, child:Text( "阴", style: TextStyle( fontSize: 20.0, color: Colors.white, ), ), ), ] ) ),
|
运行结果是:

接下来我们需要显示3天天气预报、天气质量以及生活建议的三个子控件,同样为了方便我们也把他们给单领出来,于是上面的Column接下来可以这样写:
1 2 3
4 5 6 7 8 9 10 11 12
13
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
body: SingleChildScrollView( child: Column( children: <Widget>[ ... Padding( padding: EdgeInsets.only( left: 15.0, right: 15.0, bottom: 15.0, ), child: Container( color: Colors.black54, child: _weatherList(), ), ), Padding( padding: EdgeInsets.only( left: 15.0, right: 15.0, ), child: Container( color: Colors.black54, child: _atmosphereList(), ), ), Padding( padding: EdgeInsets.only( top: 15.0, left: 15.0, right: 15.0, bottom: 15.0, ), child: Container( color: Colors.black54, child: _lifestyleList(), ), ), ] ) ),
|
3天天气预报
对于3天天气预报我们主要通过ListView来实现,由于到时候数据比较灵活,所以我们就直接使用ListView.Builder来实现:
1 2 3
4 5 6 7 8 9 10 11 12
13
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
List<String> dates = ["2019/10/01", "2019/10/02", "2019/10/03"]; List<String> temperatures = ["29/14", "30/18", "29/18"]; List<String> texts = ["阴", "晴", "雨"];
Widget _weatherList() { return Column( children: <Widget>[ Align( alignment: Alignment.centerLeft, child: Text( "预报", style: TextStyle( color: Colors.white, fontSize: 20.0, ), ), ), ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: dates.length, itemBuilder: (BuildContext context, int index) { return ListTile( title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max, children: <Widget>[ Text( "${dates[index]}", style: TextStyle(color: Colors.white), ), Text( "${temperatures[index]}", style: TextStyle(color: Colors.white), ), Text( "${texts[index]}", style: TextStyle(color: Colors.white), ), ], ), ); }), ], ); }
|
这个时候运行结果是:

空气质量
这块我们需要用到GridView,由于只有两个显示内容,而且我们后期也没有需要动态添加的需求,所以我们就直接使用GridView构造方法,而不去使用GridView的builder方法:
1 2 3
4 5 6 7 8 9 10 11 12
13
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
List<String> atmospheres = ["16", "56"];
Widget _atmosphereList() { return Column( children: <Widget>[ Align( alignment: Alignment.centerLeft, child: Text( "空气质量", style: TextStyle( color: Colors.white, fontSize: 20.0, ), ), ), GridView( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 2 ), children: <Widget>[ Column( children: <Widget>[ Text( "${atmospheres[0]}", style: TextStyle( color: Colors.white, fontSize: 40.0, ), ), Text( "能见度", style: TextStyle( color: Colors.white, fontSize: 20.0, ), ), ], ), Column( children: <Widget>[ Text( "${atmospheres[1]}", style: TextStyle( color: Colors.white, fontSize: 40.0, ), ), Text( "湿度", style: TextStyle( color: Colors.white, fontSize: 20.0, ), ), ], ), ], ), ], ); }
|
运行后的效果是:

生活建议
这块和3天天气预报一样,而且数据比3天天气预报更多,所以他更适合用ListView.builder:
1 2 3
4 5 6 7 8 9 10 11 12
13
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
List<String> _lifestyleWeatherBrf = [ "较舒适", "较舒适", "适宜", "适宜", "弱", "较适宜", "中" ]; List<String> _lifestyleWeatherTxt = [ "白天天气晴好,早晚会感觉偏凉,午后舒适、宜人。", "建议着薄外套、开衫牛仔衫裤等服装。年老体弱者应适当添加衣物,宜着夹克衫、薄毛衣等。", "各项气象条件适宜,无明显降温过程,发生感冒机率较低。", "天气较好,赶快投身大自然参与户外运动,尽情感受运动的快乐吧。", "天气较好,但丝毫不会影响您出行的心情。温度适宜又有微风相伴,适宜旅游。", "紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。", "较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。", ",气象条件对空气污染物稀释、扩散和清除无明显影响,易感人群应适当减少室外活动时间。" ];
Widget _lifestyleList() { return Column( children: <Widget>[ Align( alignment: Alignment.centerLeft, child: Text( "生活建议", style: TextStyle( color: Colors.white, fontSize: 20.0, ), ), ), ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: _lifestyleWeatherBrf.length, itemBuilder: (BuildContext context, int index) { return ListTile( title: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( "${_lifestyleWeatherBrf[index]}", style: TextStyle( color: Colors.white, ), ), Text( "${_lifestyleWeatherTxt[index]}", style: TextStyle( color: Colors.white, ), ), ], ), ); }, ), ], ); }
|
运行结果是:
