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 ... 
