基于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.2 shared_preferences

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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/591244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sentinel相关面试题及答案

数据结构和算法 1、什么是哨兵值&#xff1f;它在算法中是如何使用的&#xff1f; 哨兵值是在计算中用作标记或信号的特殊值&#xff0c;通常用于指示数据结构的边界或结束&#xff0c;或者作为检测特定条件的触发器。在算法中&#xff0c;哨兵值的使用可以简化代码并提高效…

知识付费小程序系统源码:轻松实现 一站啊运营模式+完整的代码包 附安装部署的矫教程

在当今社会&#xff0c;知识的价值愈发凸显。人们对于优质内容的渴求&#xff0c;使得知识付费市场不断扩大。然而&#xff0c;对于许多内容创作者而言&#xff0c;搭建和维护一个知识付费平台的成本较高&#xff0c;技术门槛也相对较高。下面小编来和大家分享一款知识付费小程…

静态类成员分配

{ // set size // allot storage // initialize pointer // set object count 这条语句将静态成员 num_strings的值初始化为0。请注意,不能在类声明中初始化静态成员变量,这 是因为声明描述了如何分配内存,但并不分配内存。您可以使用这种格式来创建对象,从而分配和初始化 内…

【Reading Notes】(2)

文章目录 FreestyleHip-hop dance and MusicProgrammerMaster Freestyle 都说人的成长有三个阶段&#xff0c;第一个阶段认为自己独一无二&#xff0c;天之骄子&#xff1b;第二个阶段发现自己原来如此渺小&#xff0c;如此普通&#xff0c;沮丧失望&#xff1b;第三阶段&#…

K8S学习指南(58)-K8S核心组件Kubelet简介

文章目录 前言一、设计思想1.1 分而治之的原则1.2 声明式管理 二、主要功能2.1 容器生命周期管理2.2 资源管理2.3 网络管理 三、内部工作原理3.1 Pod描述同步3.2 容器运行时接口3.3 健康检查和自愈 四、常见的故障排查4.1 日志分析4.2 资源不足4.3 网络问题 五、总结 前言 Kub…

软考高级哪个简单?

对于没有相关知识基础的考生而言&#xff0c;软考高级考试具有一定的难度。软考高级考试包括五个科目&#xff0c;分别是信息系统项目管理师、系统分析师、系统规划与管理师、系统架构设计师以及网络规划设计师。 不同科目的难易度并不会相差太大&#xff0c;不过在高级考试中&…

为什么德国如此重视可持续性有机葡萄酒种植?

可持续性在德国葡萄栽培中越来越重要&#xff0c;它包括对葡萄酒行业的生态、经济和社会问题给予同等的考虑。在过去的几年里&#xff0c;世界范围内出现了许多不同的可持续葡萄酒生产项目。 以可持续发展为导向的酒庄是如何运营的&#xff1f;作为可持续发展整体方法的一部分&…

「MySQL运维常见问题及解决方法」

「MySQL运维常见问题及解决方法」 一、查看MySQL数据库安装路径1.1、方式一 --SHOW VARIABLES LIKE basedir;1.2、方式二 --ps -ef | grep mysql 二、MySQL设置连接数与最大并发数2.1、永久生效--修改my.cnf文件2.2、临时生效--通过命令设置的全局变量 三、其他相关参数设置四、…

一文初识Linux进程(超详细!)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;HEART BEAT—YOASOBI 2:20━━━━━━️&#x1f49f;──────── 5:35 &#x1f504; ◀️ ⏸ ▶️ ☰ …

信息安全评估

评估基础 安全评估是什么? 是针对潜在影响正常执行其职能的行为产色产生干扰或破坏的因素进行识别、评价的过程 广义上是综合的包括测试、检测、测评、审核、评估检查等进行综合评价和预测&#xff1b;狭义的就是某个信息安全风险风评 为什么要做安全评估&#xff1f; 是…

3个值得推荐的WPF UI组件库

WPF介绍 WPF 是一个强大的桌面应用程序框架&#xff0c;用于构建具有丰富用户界面的 Windows 应用。它提供了灵活的布局、数据绑定、样式和模板、动画效果等功能&#xff0c;让开发者可以创建出吸引人且交互性强的应用程序。 HandyControl HandyControl是一套WPF控件库&…

DevC++ easyx实现视口编辑--像素绘图板与贴图系统

到了最终成果阶段了&#xff0c;虽然中间有一些代码讲起来没有意思&#xff0c;纯靠debug,1-1解决贴图网格不重合问题&#xff0c;这次是一个分支结束。 想着就是把瓦片贴进大地图里。 延续这几篇帖子&#xff0c;开发时间也从2023年的4月16到了6月2号&#xff0c;80小时基本…

机器学习(二) -- 数据预处理(2)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 未完待续…… 目录 系列文章目录 前言 四、【数据清洗】 1、缺失数据的检测与处理 1.1、检测与统计 1.2、处理 1.2.1、删除缺…

Postgresql源码(119)PL/pgSQL中ExprContext的生命周期

前言 在PL/pgSQL语言中&#xff0c;执行任何SQL都需要通过SPI调用SQL层解析执行&#xff0c;例如在SQL层执行表达式的入口&#xff1a; static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,PLpgSQL_expr *expr,Datum *result,bool *isNull,Oid *rettype,int32 *re…

助力成长的开源项目 —— 筑梦之路

闯关式 SQL 自学&#xff1a;sql-mother 免费的闯关式 SQL 自学教程网站&#xff0c;从 0 到 1 带大家掌握常用 SQL 语法&#xff0c;目前一共有 30 多个关卡&#xff0c;希望你在通关的时候&#xff0c;变身为一个 SQL 高手。除了闯关模式之外&#xff0c;这个项目支持自由选…

德艺双馨,以“舞”育人——《幼儿舞蹈与创编》课改总结

一个学校的发展和建设离不开教育教学质量的支撑&#xff0c;而教学改革就是走在学校发展大道上的首面旗帜&#xff0c;其中的课改更是起到以评促建的一项关键的质量工程。非常荣幸能成为我校第二批大规模课改的一员&#xff0c;我感到无比的自豪&#xff01;从参与课改到在教学…

Serverless架构学习路线及平台对比

在云计算领域&#xff0c;Serverless架构已经成为了一个重要的趋势。本文将为你提供一条清晰的Serverless架构学习路线&#xff0c;帮助你系统地掌握这个领域的知识&#xff0c;并对比国内外的Serverless平台的优缺点。 一、基础理论学习 首先&#xff0c;我们需要理解Server…

LeetCode1523. Count Odd Numbers in an Interval Range

文章目录 一、题目二、题解 一、题目 Given two non-negative integers low and high. Return the count of odd numbers between low and high (inclusive). Example 1: Input: low 3, high 7 Output: 3 Explanation: The odd numbers between 3 and 7 are [3,5,7]. Exam…

【大数据】Doris 数仓使用规范原则

第一部分:字符集规范 【强制】数据库字符集指定utf-8,并且只支持utf-8。 命令规范 【建议】库名统一使用小写方式,中间用下划线(_)分割,长度62字节内 【建议】表名称大小写敏感,统一使用小写方式,中间用下划线(_)分割,长度64字节内 第二部分:建表规范 【强制】确…

VuePress、VuePress-theme-hope 搭建个人博客 1【快速上手】 —— 防止踩坑篇

vuePress官网地址 &#x1f449; 首页 | VuePress 手动安装 这一章节会帮助你从头搭建一个简单的 VuePress 文档网站。如果你想在一个现有项目中使用 VuePress 管理文档&#xff0c;从步骤 3 开始。 步骤 1: 创建并进入一个新目录 mkdir vuepress-starter cd vuepress-star…