基于Flutter构建小型新闻App
目录
1. 概述
1.1 功能概述
1.2 技术准备
1.3 源码地址
2. App首页
2.1 pubspec依赖
2.2 热门首页组件
2.2.1 DefaultTabController
2.2.2 Swiper
2.3 新闻API数据访问
2.4 热门首页效果图
3. 新闻分类
3.1 GestureDetector
3.2 新闻分类效果图
4. 收藏功能
4.1 fluttertoast
4.3 收藏效果图
5. 相关文档和总结
最近在研究基于Flutter构建一个简单的新闻资讯app,主要参考:用Flutter极速构建原生应用(需要电子书的话可以私聊),但是这本书有部分代码引用的组件版本已经不适用所以做了一些调整。以下是对于开发过程和遇到的一些问题的总结。
1. 概述
1.1 功能概述
对于Flutter的环境配置就不做总结了,为了节省时间大家可以用IntelliJ IDEA专业版,里面就可以创建Flutter项目。主要包含三个部分:
- 热门新闻列表。
- 分类新闻列表。
新闻详情页。- 收藏功能。
1.2 技术准备
应用的首页用来展示热门新闻,我们可以选取天行数据的“综合新闻”接口服务,接口服务详细信息地址:综合新闻API接口 - 天行数据TianAPI。
在这注册一个账号用来做app测试。

然后创建一个Flutter应用,目录架构如下:

1.3 源码地址
项目源码:基于Flutter构建的新闻App。
Git地址:GitHub - BAStriver/flutter_test: test。
2. App首页
这里主要引用到DefaultTabController组件和Swiper组件。
- 使用DefaultTabController组件来实现多模块的聚合页面。
- 使用Swiper实现首页图片轮播。
首先在category和home文件夹下新建两个Dart文件,命名为category_view.dart与home_view.dart。目前实现如下效果:

2.1 pubspec依赖
如下是这个app的主要依赖设置:
dependencies:flutter:sdk: flutter# The following adds the Cupertino Icons font to your application.# Use with the CupertinoIcons class for iOS style icons.cupertino_icons: ^1.0.6http: ^1.1.2fluttertoast: ^8.2.4shared_preferences: ^2.2.2
2.2 热门首页组件
2.2.1 DefaultTabController
Widget _containerView(BuildContext context) {return Container(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: DefaultTabController(length: 2,child: Scaffold(appBar: AppBar(bottom: const TabBar(tabs: [Tab(child: Text("popular",style: TextStyle(color: Colors.black),),),Tab(child: Text("classifications",style: TextStyle(color: Colors.black),),)],indicatorColor: Colors.green,),title: const Text("ALl News",style: TextStyle(color: Colors.black),textAlign: TextAlign.center,),backgroundColor: Colors.white,actions: <Widget>[GestureDetector(child: Container(width: 60,child: const Icon(Icons.collections),),onTap: () {print('enter collection view.');Navigator.push(context, MaterialPageRoute(builder: (context) {print('enter collection view2.');return CollectionView();}));},)],),body: TabBarView(children: [HomeView(), CategoryView()],),)),);}
2.2.2 Swiper
Widget _buildSwiper(BuildContext context) {return Container(height: 150,child: Swiper(pagination: const SwiperPagination(),control: const SwiperControl(),autoplay: true,itemCount: 3,itemBuilder: (BuildContext context, int index) {return Container(margin: const EdgeInsets.only(bottom: 5),color: Colors.orange,width: MediaQuery.of(context).size.width,height: 150,child: Image.network(_dataList[_dataList.length - 1].picUrl,height: 150,width: MediaQuery.of(context).size.width,fit: BoxFit.fitWidth,),);},),);}
这里如果参考书上的写法会有问题,所以为了解决升级到Dart3.0后Swiper不兼容的问题可以参考:由于flutter_app依赖于flutter_swiper>=0.0.2,不支持零安全,版本解决失败。_because book depends on flutter_swiper >=0.0.2 whi-CSDN博客
2.3 新闻API数据访问
Future<HomeModel> queryHomeData(int page) async {var url = Uri.parse('https://apis.tianapi.com/generalnews/index');var headers = <String, String>{"Access-Control-Allow-Origin": "*","Content-Type":"application/x-www-form-urlencoded"};var body = <String, String>{"key": URL_KEY, "num": "10", "page": "$page"};final response = await http.post(url,body: body, headers: headers);Map<String, dynamic> jsonMap = json.decode(response.body);return HomeModel.fromJson(jsonMap);}
这里会涉及到数据模型的构建和加载,可以参考:A value of type 'X' can't be assigned to a variable of type 'List' - 糯米PHP
2.4 热门首页效果图

3. 新闻分类
这里主要引用到GestureDetector组件。
关于新闻分类主页采用网格布局,根据天行数据网上提供的新闻接口定义10个分类。
final List<String> _categorys = ["GeneralNews","CarNews","DomesticNews","AnimeNews","FinancialNews","GameNews","InternationalNews","AINews","MilitaryNews","SportNews"];
3.1 GestureDetector
点击一个分类,页面会跳转到分类列表页。
关于详细的组件回调事件可以参考:
Flutter--GestureDetector手势识别组件_flutter gesturedetector-CSDN博客
Widget _getItem(BuildContext context, int index) {return Container(width: MediaQuery.of(context).size.width,height: 130,child: Row(children: <Widget>[GestureDetector(child: Container(width: MediaQuery.of(context).size.width / 2,color: index % 2 == 0 ? Colors.orange : Colors.blueAccent,height: 130,child: Center(child: Text(_categorys[index * 2],textAlign: TextAlign.center,style: const TextStyle(fontSize: 30, color: Colors.white),),),),onTap: () {Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {return CategoryListView(CATEGORY_KEYS[_categorys[index * 2]]!, _categorys[index * 2]);}));},),GestureDetector(child: Container(width: MediaQuery.of(context).size.width / 2,color: index % 2 == 0 ? Colors.blueAccent : Colors.orange,height: 130,child: Center(child: Text(_categorys[index * 2 +1],textAlign: TextAlign.center,style: const TextStyle(fontSize: 30, color: Colors.white),),),),onTap: () {Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {return CategoryListView(CATEGORY_KEYS[_categorys[index * 2 + 1]]!, _categorys[index * 2 + 1]);}));},)],),);}
3.2 新闻分类效果图

进入一般新闻的分类页面:

4. 收藏功能
这里主要引用了fluttertoast和shared_preferences组件。
- fluttertoast实现弹窗效果。
- shared_preferences实现本地缓存。
4.1 fluttertoast
实现弹窗是否收藏新闻。
Widget _buildItem(BuildContext context, int index) {return Container(height: 110,width: MediaQuery.of(context).size.width,margin: const EdgeInsets.only(bottom: 1),color: Colors.amber,child: GestureDetector(child: Row(children: <Widget>[Container(color: Colors.grey,child: Image.network(// "http://n.sinaimg.cn/sinakd202124s/162/w550h412/20210204/6706-kirmait9301473.jpg",_dataList[index].picUrl,width: 130,height: 110,fit: BoxFit.fitHeight,),),Column(crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[Container(margin: const EdgeInsets.only(left: 10, top: 10, right: 10),width: MediaQuery.of(context).size.width - 130 - 20,child: Text(_dataList[index].title,overflow: TextOverflow.ellipsis,maxLines: 2,style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),),),Container(margin: const EdgeInsets.only(left: 10, top: 5),child: Text(_dataList[index].description),),Container(margin: const EdgeInsets.only(left: 10, top: 5),child: Text(_dataList[index].ctime),),],)],),onLongPress: () {showDialog(context: context,builder: (BuildContext context) {return AlertDialog(title: Text('Do you want to save it ?'),actions: <Widget>[TextButton(onPressed: () {_addCollection(_dataList[index].id, _dataList[index].title);Navigator.of(context).pop();},child: Text('Yes')),TextButton(onPressed: () {Navigator.of(context).pop();},child: Text('No')),],);});},),);}
4.2 shared_preferences
实现本地缓存。
void _addCollection(String id, String title) async {print('id: ' + id);print('title: ' + title);SharedPreferences? preferences = await SharedPreferences.getInstance();String? data = preferences.getString(id);if (data == null) {await preferences.setString(id, title);Fluttertoast.showToast(msg: 'saved successfully.');} else {Fluttertoast.showToast(msg: 'the new already existing.');}}
4.3 收藏效果图
长按新闻。

收藏成功。

点击主页右上角的收藏图标进入收藏页面:

5. 相关文档和总结
开发体验初探 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
如何搭建flutter开发环境_flutter环境搭建-CSDN博客
Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用
注:
1.遇到跨域问题访问不到图片可以参考:
开发环境如需解决跨域问题
flutter for web 带cookie的网络请求跨域问题处理
2.关于GestureDetector组件的用法可以参考:Flutter--GestureDetector手势识别组件
3.关于SharedPreferences组件的用法可以参考:Flutter 数据存储--shared_preferences使用详情
4.遇到:MissingPluginException (MissingPluginException(No implementation found for method... 需要重新启动项目。
5.遇到Navigator无法正常跳转页面时候可以参考:
Flutter无法跳转页面的解决方法(push方法无效) - 简书
Navigator的正确打开方式-CSDN博客
比如第4节开发收藏功能的时候遇到这个无法跳转的问题,main.dart做了如下的修改:

6.Flutter有两个App主题风格可以引用,包括:cupertino(IOS)、material(Android),具体的官方文档可以参考:Cupertino (iOS-style) widgets | Flutter 、Material Components widgets | Flutter。
关于这些风格的Widgets用法也可以参考:开启Fluter基础之旅<三>-------Material Design风格组件、Cupertino风格组件、Flutter页面布局篇...-CSDN博客
相关文章:
基于Flutter构建小型新闻App
目录 1. 概述 1.1 功能概述 1.2 技术准备 1.3 源码地址 2. App首页 2.1 pubspec依赖 2.2 热门首页组件 2.2.1 DefaultTabController 2.2.2 Swiper 2.3 新闻API数据访问 2.4 热门首页效果图 3. 新闻分类 3.1 GestureDetector 3.2 新闻分类效果图 4. 收藏功能 4…...
利用python将excel文件转成txt文件,再将txt文件上传hdfs,最后传入hive中
将excel文件转成txt文件,再将txt文件上传hdfs,最后传入hive中 注意的点 (1)先判断写入的txt文件是否存在,如果不存在就需要创建路径 (2)如果txt文件已经存在,那么先将对应的文件进行…...
【自学笔记】01Java基础-07面向对象基础-02继承
记录学习Java基础中有关继承、方法重写、构造器调用的基础知识,学习继承之前建议学习static关键字的内容【自学笔记】01Java基础-09Java关键字详解 1 继承概述 1.1 什么是继承? 1.2 继承的特点 子类可以继承父类的属性和行为,但是子类不能…...
二分查找(一)
算法原理 原理:当一个序列有“二段性”的时候,就可以使用二分查找算法。 适用范围:根据规律找一个点,能将这个数组分成两部分,根据规律能有选择性的舍去一部分,进而在另一个部分继续查找。 除了最普通的…...
【华为OD真题 Python】精准核酸检测
文章目录 题目描述输入描述输出描述示例1输入输出说明备注代码实现题目描述 为了达到新冠疫情精准防控的需要,为了避免全员核酸检测带来的浪费,需要精准圈定可能被感染的人群。 现在根据传染病流调以及大数据分析,得到了每个人之间在时间、空间上是否存在轨迹的交叉。 现…...
Springboot使用logback
文章目录 目录 文章目录 前言 一、添加依赖 二、使用步骤 三 、测试使用 总结 前言 Logback 是log4j 框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境,同时天然支持 SLF4J。 Logback 的定制性更加灵活,同时也是 Sprin…...
【REST2SQL】03 GO读取JSON文件
REST2SQL需要一些配置信息,用JSON文件保存,比如config.json 1 创建config.json配置文件 {"hostPort":"localhost:5217","connString":"oracle://blma:5217127.0.0.1:1521/CQYH","_oracle":"ora…...
数据库-MySQL 启动方式
以管理员身份运行命令行 或者Shell net start //查看所有服务 net start MYSQL80 //启动服务 net stop MYSQL80 //停止服务完整安装MySQL社区版本的 会有这个 启动服务 停止服务 重启服务...
YAML使用
yaml yaml是类型aml,json的标记性语言,它强调以数据为中心 yaml的语法主要是如下几个: 大小写敏感 使用缩进表示层级关系 缩进不允许使用tab、只允许空格(低版本限制,高版本不限制) 缩进时空格数不重要&a…...
读书之深入理解ffmpeg_简单笔记2(初步)
再回看第一遍通读后的笔记,感觉还有很多的细节需要一一攻克,。 mp4的封装格式,解析方式。 flv的封装格式,解析方式。 ts的封装格式,解析方式。 第四章 封装和解封装 4.2 视频文件转flv (头文件和文件内容࿰…...
ELK+kafka+filebeat企业内部日志分析系统搭建
看上面的拓扑图,我们至少准备七台机器进行下面的实验项目。 机器主要作用分布如下: 三台安装elasticsearch来搭建ES集群实现高可用,其他机器就依次安装filebeat,kafka,logstash和kibana软件 一、部署elasticsearch来搭建ES集群 1.安装jdk 由于ES运行…...
勒索检测能力升级,亚信安全发布《勒索家族和勒索事件监控报告》
评论员简评 近期(12.08-12.14)共发生勒索事件119起,相较之前呈现持平趋势。 与上周相比,近期仍然流行的勒索家族为lockbit3和8base。在涉及的勒索家族中,活跃程度Top5的勒索家族分别是:lockbit3、siegedsec、dragonforce、8base和…...
编译原理复习的有用链接
2024年1月7日,考完编译原理,是时候和考试时候的她说再见了,整理一些收藏夹里的链接和思考吧 实验看这里: 编译原理_HNU岳麓山大小姐的博客-CSDN博客 课后习题看这里: 编译原理作业答案github LL1文法复习 [编译原…...
不带控制器打包exe,转pdf文件时失败的原因
加了注释的两条代码后,控制器会显示一个docx转pdf的进度条。这个进度条需要控制器的实现,如果转exe不带控制器的话,当点击转换为pdf的按钮就会导致程序出错和闪退。 __init__.py文件的入口...
Python 注释的方法
在Python中,有两种常见的注释方法: 单行注释:使用#符号来注释一行代码。在#符号后面的内容将被视为注释,不会被解释器执行,如: # 这是一个单行注释 print(hello world!) # 打印字符串多行注释࿱…...
webman插件创建
webman插件创建 介绍 应用插件实际上是一个完整的应用,它能以插件的形式安装到主项目中,使主项目快速获得某个模块功能。 例如:主项目需要一个问答系统,则可以安装一个问答应用插件,需要一个商城系统,则安…...
大模型迎来“AppStore时刻”,OpenAI给2024的新想象
一夜之间,OpenAI公布了多个重磅消息,引发市场关注。 钛媒体App 1月5日消息,今晨,OpenAI公司向所有GPT开发者们发布一封邮件称,下周将上线自定义的“GPT Store”商店,这有望推动ChatGPT开发者生态不断完善。…...
ubuntu解决在pycharm上使用jupyter无法导入虚拟环境中的包的问题
ubuntu解决在pycharm上使用jupyter无法导入虚拟环境中的包的问题 根本原因是jupyter 没有和他对应的kernel 需要先使用命令行建立kernel 下载ipykernel pip install ipykernel 首先激活conda conda activate然后添加你的kernel到虚拟环境 python -m ipykernel install -…...
探索2024年软件测试的几大主导趋势
进入2024年,考虑影响测试环境的问题至关重要。这种思考将成为团队了解主要瓶颈和实现当今不断提高的期望的首要因素。 01 了解关键测试瓶颈 毋庸置疑,现代团队需要不断创新、适应和拥抱最新趋势,以保持竞争力并提供以客户为中心的解决方案。尽…...
Linux C语言 48-信号总结
Linux C语言 48-信号总结 本节关键字:Linux、C语言、常用信号 相关C库函数:printf、signal、kill Linux中都有哪些信号 信号在Linux操作系统中是很重要的,信号的产生方式可以是来自键盘、由软件条件产生、调用硬件异常产生。来自系统函数调…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...
客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...
