基于Flutter构建小型新闻App

news2024/11/26 5:54:53

目录

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.6
  http: ^1.1.2
  fluttertoast: ^8.2.4
  shared_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.coloradmin.cn/o/1349830.html

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

相关文章

【快速全面掌握 WAMPServer】11.安装 PHP 扩展踩过的坑

网管小贾 / sysadm.cc 我们在调试程序代码时&#xff0c;总会遇到一些 PHP 项目需要某些扩展组件。 而在 WAMPServer 下通常的 PHP 扩展的安装也不算有多麻烦。 具体关于 PHP 扩展的区分&#xff08;比如安全线程或非安全线程&#xff09;&#xff0c;以及怎么安装小伙伴们可…

多任务并行处理相关面试题

我自己面试时被问过两次多任务并行相关的问题&#xff1a; 假设现在有10个任务&#xff0c;要求同时处理&#xff0c;并且必须所有任务全部完成才返回结果 这个面试题的难点是&#xff1a; 既然要同时处理&#xff0c;那么肯定要用多线程。怎么设计多线程同时处理任务呢&…

【Linux】—— 匿名管道

前言&#xff1a; 接下来我将带大家探索 进程间通信 的方式。本期&#xff0c;要讲的就是管道其中之一“匿名管道”&#xff01;&#xff01; 目录 &#xff08;一&#xff09;进程间通信介绍 1、进程间通信目的 2、进程间通信发展 3、进程间通信分类 &#xff08;二&…

2024年01月编程语言流行度排名

点击查看最新编程语言流行度排名&#xff08;每月更新&#xff09; 2024年01月编程语言流行度排名 编程语言流行度排名是通过分析在谷歌上搜索语言教程的频率而创建的 一门语言教程被搜索的次数越多&#xff0c;大家就会认为该语言越受欢迎。这是一个领先指标。原始数据来自…

linux休眠机制介绍

一、概述 Linux系统提供了休眠和低功耗模式&#xff0c;可以帮助节省电力和延长电池寿命&#xff0c;休眠对应的另外一种模式就是唤醒。 二、常用的休眠方式 常用的休眠方式有freeze,standby, mem, disk&#xff0c;hibernate freeze: 冻结所有的进程&#xff0c;包括用户空…

微信小程序-页面开发

文章目录 微信小程序第二章2. 页面开发2.1 创建开发页面2.2 修改项目首页2.3 页面的结构和样式设计2.3.1 WXML结构设计2.3.1.1 什么是WXML2.3.1.2 WXML的常见标签2.3.1.3 WXML的特点 2.3.2 WXSS样式设计2.3.2.1 什么是WXSS 2.4 组件库的使用和自定义组件2.4.1 小程序中的组件分…

基于 Haar 特征的人脸检测

Haar分类器概述 Haar分类器采用的是Viola-Jones人脸检测算法 该算法需要用到大量的积极图片&#xff08;包含人脸的图片&#xff09;和消极图片&#xff08;不包含人脸的图片&#xff09;&#xff0c;从中提取类Haar特征( Haar-like features)&#xff0c;经过反复训练&#…

TF-IDF(Term Frequency-Inverse Document Frequency)算法 简介

TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种用于信息检索和文本挖掘的常用算法。它用于评估一个词对于一个文档集合中某个文档的重要性。 这个算法的基本思想是&#xff1a;如果一个词在一个文档中频繁出现&#xff0c;并且在整个文档集合…

MySQL基础笔记(1)基础理论

一.基本概念 DB&#xff1a;数据库&#xff0c;存储数据的仓库&#xff0c;数据是有组织地进行存储DBMS&#xff1a;操纵和管理数据库的大型软件 SQL&#xff1a;结构化查询语言&#xff0c;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库的统一标准 &…

CSDN规则详解(二)

文章目录 每日一句正能量前言博文发布说明创建专栏显示数量上限问题私信规则说明会员下载次数说明后记 每日一句正能量 与其战胜敌人一万次&#xff0c;不如战胜自己一次。 前言 在数字技术的浪潮下&#xff0c;中国的开发者社区蓬勃发展&#xff0c;而CSDN则是其中的佼佼者。…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

我在CSDN的2023年

一、引言 在2023年的这一年当中&#xff0c;在CSDN的生活让我得到许多知识与启发&#xff0c;也让我获得一些快乐和成就 二、自己的收获 在这一年当中&#xff0c;我从一个只会看别人写的文章解决问题到&#xff0c;可以自己写文章帮别人解决问题&#xff0c;这种成就感是极大…

JavaScript history对象常用方法【通俗易懂】

✨前言✨   本篇文章主要在于了解及使用JavaScript中history对象常用方法 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区留言 文章目录 一&…

【熔断限流组件resilience4j和hystrix】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟…

thinkphp6入门(14)-- 多关联模型查询

背景&#xff1a; 有3个数据表&#xff0c;一个User表&#xff0c;一个Cloth表&#xff0c;一个Shoe表。 Cloth表和Shoe表分别和User表通过user_id关联。 thinkphp 6中如何通过模型查询所有用户&#xff0c;其中包括每个用户的cloth和shoe。 多关联模型查询&#xff1a; 1.…

热塑性塑料激光透光率检测仪

热塑性塑料是一类在一定温度下具有可塑性&#xff0c;冷却后固化且能重复这种过程的塑料。它们通常由线型高分子化合物组成&#xff0c;这些分子在受热时不会发生交联。热塑性塑料可以通过回收和再加工&#xff0c;重新制成新的产品。根据其性能特点、用途广泛性和成型技术通用…

YOLOv8改进 | 2023主干篇 | FasterNeT跑起来的主干网络( 提高FPS和检测效率)

一、本文介绍 本文给大家带来的改进机制是FasterNet网络&#xff0c;将其用来替换我们的特征提取网络&#xff0c;其旨在提高计算速度而不牺牲准确性&#xff0c;特别是在视觉任务中。它通过一种称为部分卷积&#xff08;PConv&#xff09;的新技术来减少冗余计算和内存访问。…

漏洞复现-海康威视网络对讲广播系统远程命令执行漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

策略模式示例,Lambda表达式

这样会有很多代码冗余 以上代码使用策略模式优化 优化方案1 那么现在可以这样 优化方案二 原先定义了接口,还需要一个个写实现类,其实完全没必要,用匿名内部类方式就可以 优化方案三:用lambda方式简写 优化方案四:不需要定义接口 使用Stream API

JavaScript中alert、prompt 和 confirm区别及使用【通俗易懂】

✨前言✨   本篇文章主要在于&#xff0c;让我们看几个与用户交互的函数&#xff1a;alert&#xff0c;prompt 和confirm的使用及区别 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&…