Flutter系列文章-实战项目
在本篇文章中,我们将通过一个实际的 Flutter 应用来综合运用最近学到的知识,包括保存到数据库、进行 HTTP 请求等。我们将开发一个简单的天气应用,可以根据用户输入的城市名获取该城市的天气信息,并将用户查询的城市列表保存到本地数据库中。
第一步:需求分析和设计
1. 确定项目目标
我们的目标是开发一个天气应用,用户可以在应用中输入城市名,然后获取该城市的天气信息。
2. 设计界面
我们的应用包含两个页面:主页面用于输入城市名,显示天气信息,以及查询历史记录页面用于显示用户查询的城市列表。
第二步:开发
1. 创建 Flutter 项目
首先,在命令行中创建一个新的 Flutter 项目:
flutter create my_first_flutter_app
然后,进入项目目录:
cd my_first_flutter_app
2. 编码实现
a. 创建页面和路由
在 lib 文件夹中创建两个文件:home_page.dart、history_page.dart。
home_page.dart:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String city = '';
String weather = '';
void getWeather() async {
final response = await http.get(Uri.parse(
'https://api.openweathermap.org/data/2.5/weather?q=$city&appid=YOUY_API_KEY'));
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
setState(() {
weather =
'Temperature: ${data['main']['temp']}°C, Weather: ${data['weather'][0]['main']}';
});
} else {
setState(() {
weather = 'Failed to get weather data';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Weather App'),
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
onChanged: (value) {
setState(() {
city = value;
});
},
decoration: InputDecoration(labelText: 'City Name'),
),
ElevatedButton(
onPressed: () {
getWeather();
},
child: Text('Get Weather'),
),
SizedBox(height: 20),
Text(
weather,
style: TextStyle(fontSize: 18),
),
],
),
),
);
}
}
history_page.dart:
import 'package:flutter/material.dart';
import 'package:my_first_flutter_app/database_helper.dart';
import 'package:my_first_flutter_app/city.dart';
class HistoryPage extends StatefulWidget {
@override
_HistoryPageState createState() => _HistoryPageState();
}
class _HistoryPageState extends State<HistoryPage> {
List<City> cities = [];
@override
void initState() {
super.initState();
fetchCities();
}
void fetchCities() async {
final dbHelper = DatabaseHelper.instance;
final allRows = await dbHelper.queryAllRows();
setState(() {
cities = allRows.map((row) => City.fromMap(row)).toList();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Search History'),
),
body: ListView.builder(
itemCount: cities.length,
itemBuilder: (context, index) {
final city = cities[index];
return ListTile(
title: Text(city.name),
subtitle: Text('Weather: ${city.weather}'),
);
},
),
);
}
}
在 main.dart 文件中,设置路由:
import 'package:flutter/material.dart';
import 'package:my_first_flutter_app/home_page.dart';
import 'package:my_first_flutter_app/history_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomePage(),
'/history': (context) => HistoryPage(),
},
);
}
}
b. 实现数据库功能
我们将使用 SQLite 来保存用户查询的城市列表。在 lib 文件夹中创建新的文件 city.dart、database_helper.dart,用于创建和管理数据库。
city.dart
class City {
int id;
String name;
String weather;
City({this.id = 0, required this.name, required this.weather});
Map<String, dynamic> toMap() {
return {'id': id, 'name': name, 'weather': weather};
}
City.fromMap(Map<String, dynamic> map)
: id = map['id'],
name = map['name'],
weather = map['weather'];
}
database_helper.dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:my_first_flutter_app/city.dart';
class DatabaseHelper {
static final _databaseName = 'weather_database.db';
static final _databaseVersion = 1;
static final table = 'cities';
static final columnId = '_id';
static final columnName = 'name';
static final columnWeather = 'weather';
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database? _database;
Future<Database?> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion, onCreate: _onCreate);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL,
$columnWeather TEXT NOT NULL
)
''');
}
Future<int> insert(City city) async {
Database? db = await instance.database;
return await db?.insert(table, city.toMap()) ?? 0;
}
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database? db = await instance.database;
return await db?.query(table) ?? [];
}
}
第三步:测试
1. 运行应用
使用以下命令在模拟器或真机上运行应用:
flutter run
检查应用是否按预期工作,并确保你可以查询城市的天气,并查看查询历史记录。

2. 进行单元测试和集成测试
除了手动测试之外,我们还应该进行单元测试和集成测试来确保应用的稳定性和正确性。在 Flutter 中,我们可以使用 flutter_test 包进行测试。
第四步:发布
当我们完成了开发和测试之后,就可以考虑发布应用了。在发布之前,我们需要完成以下几个步骤:
1. 生成 APK 或 IPA 文件
使用以下命令生成 APK 文件(Android)或 IPA 文件(iOS):
flutter build apk
flutter build ios
2. 申请开发者账号
如果你要发布到应用商店(如 Google Play 或 App Store),你需要申请开发者账号,并遵循相应的发布指南。
3. 提交应用
一旦你准备好了 APK 或 IPA 文件,并且已经申请了开发者账号,你可以提交应用到相应的应用商店进行审核和发布。
总结
在本篇文章中,我们通过一个简单的天气应用示例,综合运用了最近学到的知识,包括保存到数据库、进行 HTTP 请求等。通过这个实战项目,你可以更加深入地了解 Flutter 应用的开发流程,并掌握实际项目中的常用技术和最佳实践。
希望这个实战项目对你有所帮助。如果你有任何问题或需要进一步的指导,请随时向我询问。祝你在 Flutter 开发的道路上取得成功!
本文由mdnice多平台发布
Flutter系列文章-实战项目的更多相关文章
- nginx高性能WEB服务器系列之五--实战项目线上nginx多站点配置
nginx系列友情链接:nginx高性能WEB服务器系列之一简介及安装https://www.cnblogs.com/maxtgood/p/9597596.htmlnginx高性能WEB服务器系列之二 ...
- 一、VUE项目BaseCms系列文章:项目介绍与环境配置
一.项目效果图预览: 二.项目介绍 基于 elementui 写一个自己的管理后台.这个系列文章的目的就是记录自己搭建整个管理后台的过程,希望能帮助到那些入门 vue + elementui 开发的小 ...
- 我的.net并发系列文章及项目经验整理
一直在关注研究.net下的并发处理,之前也发布过几篇文章,今天就都整理下. 使用BlockingCollection来做并发处理,同时增加并发队列来做并发处理时的退出判断: 你真的知道.NET Fra ...
- 二、VUE项目BaseCms系列文章:项目目录结构介绍
一. 目录结构截图 二. 目录结构说明 - documents 存放项目相关的文档文件 - api api 数据接口目录 - assets 资源文件目录 - components ...
- 【Flutter 实战】17篇动画系列文章带你走进自定义动画
老孟导读:Flutter 动画系列文章分为三部分:基础原理和核心概念.系统动画组件.8篇自定义动画案例,共17篇. 动画核心概念 在开发App的过程中,自定义动画必不可少,Flutter 中想要自定义 ...
- 开篇:软件项目的整个流程 - IT软件人员学习系列文章
这段时间闲来无事,就在总结以前的项目经验,然后写成博客的形式以进行记录.本文就对<IT软件人员学习系列文章>做个开篇吧. 对于IT软件的开发来说,无外乎B/S.C/S和Android.iO ...
- Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览
关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...
- QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL
QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL 课程1 C语言程序设计高级实用速成课程 基础+进阶+自学 课程2 C语言程序设计Windows GDI图形绘 ...
- Rancher 系列文章-Rancher 对接 Active Directory 实战
概述 只要是个公司,基本上都有邮箱和 AD(Active Directory). 在 AD 里,已经有了: 用户 账号密码 邮箱 用户组 组织架构 所以对于一些仅限于本公司一定范围内人员使用的管理或后 ...
- Velero系列文章(四):使用Velero进行生产迁移实战
概述 目的 通过 velero 工具, 实现以下整体目标: 特定 namespace 在B A两个集群间做迁移; 具体目标为: 在B A集群上创建 velero (包括 restic ) 备份 B集群 ...
随机推荐
- 程序员IT行业,外行眼里高收入人群,内行人里的卷王
程序员 一词,在我眼里其实是贬义词.因为我的其他不是这行的亲朋友好友,你和他们说,你是一名程序员· 他们 第一刻板影响就是,秃头,肥胖,宅男,油腻,不修边幅 反正给人一种不干净,不好形象,,,,不知道 ...
- 2020-10-13:hash与B+tree的区别?
福哥答案2020-10-13: [答案来自知乎:](https://www.zhihu.com/question/425378511/answer/1522000015) 这里我从数据库索引的层面回答 ...
- 【C++】requires关键字简介
requires 是 C++20 中引入的一个新关键字,用于在函数模板或类模板中声明所需的一组语义要求,它可以用来限制模板参数,类似于 typename 和 class 关键字. requires关键 ...
- MMCM/PLL VCO
输入输出时钟频率,input 322.265625Mhz, output 312.5Mhz 对于使用MMCM与PLL的不同情况,虽然输入输出频率是一样的,但是,分/倍频系数是不同的,不能使用同一套参数 ...
- Python对两个Excel操作
简介 现在有个需求,我们根据需要 data.xlsx 中某些单元格的内容来查找 find.xlsx 中的某些內容. 数据内容(为了数据安全,所有数据均已模糊处理) data.xlsx内容: find. ...
- 警惕看不见的重试机制:为什么使用RPC必须考虑幂等性
0 文章概述 在RPC场景中因为重试或者没有实现幂等机制而导致的重复数据问题,必须引起大家重视,有可能会造成例如一次购买创建多笔订单,一条通知信息被发送多次等问题,这是技术人员必须面对和解决的问题. ...
- shader编程经典:分形--科赫曲线
序言 科赫(雪花)曲线是一个经典分形图案,来一起领略下分形之美.本篇内容用到一些基础的内容,例如UV的理解和画线技巧,有需要的话可以参考合集的画圆和画线两篇文章. 示例 shadertoy 代码: # ...
- R画韦恩图之总结
本文分享自微信公众号 - 生信科技爱好者(bioitee).如有侵权,请联系 support@oschina.cn 删除.本文参与"OSC源创计划",欢迎正在阅读的你也加入,一起分 ...
- 【Python&GIS】判断图片中心点/经纬度点是否在某个面内
Python的exifread库可以获取图片中的源数据信息,包括经纬度.相机厂商.曝光时间.焦距.拍摄时间.拍摄地点等等信息.我们可以通过exifread库从图片中获取图片的经纬度,再通过shape ...
- js递归生成树形结构
先看结果 结果 [ { "id": 1, "title": "张三", "parent_id": -1, "c ...