热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Flutter简易新闻项目

目标使用flutter快速开发Android和iOS的简易的新闻用户端API使用的是showapi(易源数据)加载热门微信文章效果比照AndroidiOSimageimageima
目标

使用flutter快速开发 Android 和 iOS 的简易的新闻用户端
API使用的是 showapi(易源数据) 加载热门微信文章

效果比照
AndroidiOS
imageimage
imageimage
imageimage
简介

这是一个建议的新闻用户端 页面非常简单

通过网络请求加载 分类数据 和 分类介绍数据 (key都在代码里了,轻量使用~)

UI上几乎是没有任何特点

使用BottomNavigationBar 分成3个控制器

首页使用DefaultTabController管理内容

相关依赖:

http: "^0.11.3" #网络请求cached_network_image: "^0.4.1" #图片加载cupertino_icons: ^0.1.2 #iconflutter_webview_plugin: ^0.1.6 #webviewshared_preferences: ^0.4.2 #持久化数据url_launcher: ^3.0.3 #调用系统浏览器代码

使用单例来保存数据

因为分类准则上是没有变化的,我这里就使用单例来保存从API请求的分类数据,减少请求次数(API请求次数有限)

class UserSinglen { List allTypes = []; static final UserSinglen _singleton = new UserSinglen._internal(); factory UserSinglen() { return _singleton; } UserSinglen._internal();}

使用Shared保存数据

保存当前选中的分类

class Shared { //保存分类 static Future saveSelectedType(List list) async { SharedPreferences pre = await SharedPreferences.getInstance(); await pre.setStringList("selectedTypeIds",list); print(list); return; } //获取已选择的分类 static Future> getSelectedType() async { SharedPreferences pre = await SharedPreferences.getInstance(); try { List typeIds = pre.getStringList("selectedTypeIds"); print("typeids = $typeIds"); return typeIds; } catch (e) { return null; } }}

BottomNavigationBar的使用

构建NavigationIcon

为底部的icon封装,方便找到对应的控制器

class NavigationIcon { NavigationIcon({ Widget icon, Widget title, TickerProvider vsync, }) : item = new BottomNavigationBarItem( icon: icon, title: title, ), cOntroller= new AnimationController( duration: kThemeAnimationDuration, vsync: vsync, ); final BottomNavigationBarItem item; final AnimationController controller;}

构建当前控制器

当前控制器是Stateful类型,刷新页面
初始化3个控制器

class Index extends StatefulWidget { const Index({ Key key }) : super(key: key); @override State createState() => new IndexState();}class IndexState extends State with TickerProviderStateMixin { List navigationIcons; List pageList; int currentPageIndex = 0; StatefulWidget currentWidget; @override void initState() { // TODO: implement initState super.initState(); navigatiOnIcons= [ new NavigationIcon( icon: new Icon(Icons.home), title: new Text("首页"), vsync: this, ), new NavigationIcon( icon: new Icon(Icons.category), title: new Text("分类"), vsync: this, ), new NavigationIcon( icon: new Icon(Icons.info), title: new Text("关于"), vsync: this, ) ]; pageList = [ new Home(key: new Key("home"),), new Category(key: new Key("category"),), new About(), ]; for(NavigationIcon view in navigationIcons) { view.controller.addListener(rebuild()); } currentWidget = pageList[currentPageIndex]; } @override void dispose() { // TODO: implement dispose super.dispose(); for (NavigationIcon view in navigationIcons) { view.controller.dispose(); } }rebuild(){ print("rebuild"); setState(() {});} @override Widget build(BuildContext context) { final BottomNavigationBar bottomNavigatiOnBar= new BottomNavigationBar( items: navigationIcons.map(((i) => i.item)).toList(), currentIndex: currentPageIndex, fixedColor: Config.mainColor, type: BottomNavigationBarType.fixed, onTap: (i) { setState(() { navigationIcons[i].controller.reverse(); currentPageIndex = i; navigationIcons[i].controller.forward(); currentWidget = pageList[i]; }); }, ); return new MaterialApp( theme: Config.themeData, home: new Scaffold( body: Center( child: currentWidget, ), bottomNavigationBar: bottomNavigationBar, ), ); }}

首页

首页实时获取存储在本地的已选择分类,与单例中的所有分类做比照,获取对应的类型id
(shared_preferences只能存储基本数据类型)

class Home extends StatefulWidget { const Home({Key key}) : super(key: key); @override State createState() { // TODO: implement createState return new HomeState(); }}class HomeState extends State { bool _isloading = true; List _list; @override void initState() { // TODO: implement initState super.initState(); API.featchTypeListData((List callback) { Shared.getSelectedType().then((onValue) { print(onValue); if (onValue != null) { print("lentch = "); print(onValue.length); setState(() { _isloading = false; _list = callback.where((t) => onValue.contains(t.id)).toList(); }); } }); }, errorback: (error) { print("error:$error"); }); } @override Widget build(BuildContext context) { // TODO: implement build return _isloading ? new Scaffold( appBar: new AppBar( title: new Text("loading..."), ), body: new Center( child: new CircularProgressIndicator( backgroundColor: Colors.black, ), ), ) : new DefaultTabController( length: _list.length, child: new Scaffold( appBar: new AppBar( title: new Text("新闻"), bottom: new TabBar( isScrollable: true, labelColor: Colors.white, unselectedLabelColor: Colors.black, tabs: _list.map((f) => new Tab(text: f.name)).toList()), ), body: new TabBarView( children: _list.map((f) => new Content(channelId: f.id)).toList(), )), ); } @override void dispose() { // TODO: implement dispose super.dispose(); }}

分类

这个页面也很简单,将已选择的分类id存进shared_preferences中就行

class Category extends StatefulWidget { const Category({Key key}) : super(key: key); @override State createState() => new CategoryState();}class CategoryState extends State { List _list = []; bool _isloading = true; @override void initState() { // TODO: implement initState super.initState(); API.featchTypeListData((List callback) { Shared.getSelectedType().then((onValue) { setState(() { _isloading = false; _list = callback .map((f) => new WeType(f.id, f.name, onValue.contains(f.id))) .toList(); }); }); }, errorback: (error) { print("error:$error"); }); } _onTapButton(WeType type) { setState(() { _list = _list.map((WeType f) { if (f.id == type.id) { f.isSelected = !f.isSelected; } return f; }).toList(); Shared.saveSelectedType( _list.where((t) => t.isSelected).map((f) => f.id).toList()); }); } _selectAll() { print("all"); var r = _list.where((t) => t.isSelected).toList(); bool res = r.length <_list.length ? true : false; setState(() { _list = _list.map((f) => new WeType(f.id, f.name, res)).toList(); Shared.saveSelectedType( _list.where((t) => t.isSelected).map((f) => f.id).toList()); }); } @override Widget build(BuildContext context) { return _isloading ? CircularProgressIndicator() : new Scaffold( appBar: new AppBar( title: new Text("分类"), actions: [ new FlatButton( onPressed: _selectAll, child: new Center( child: new Text( "全选", style: new TextStyle(color: Colors.white), )), ) ], ), body: new GridView.count( // Create a grid with 2 columns. If you change the scrollDirection to // horizontal, this would produce 2 rows. crossAxisCount: 3, crossAxisSpacing: 0.0, childAspectRatio: 2.0, // Generate 100 Widgets that display their index in the List children: _list .map((f) => new FlatButton( padding: const EdgeInsets.all(10.0), onPressed: () { _onTapButton(f); }, child: new Center(child: _Button(f.name, f.isSelected)), )) .toList(), )); }}class _Button extends StatelessWidget { final String title; final bool isSelected; _Button(this.title, this.isSelected); @override Widget build(BuildContext context) { // TODO: implement build return new Stack( alignment: AlignmentDirectional.center, children: [ new Container( child: new Center( child: new Text(title), ), decoration: new BoxDecoration( color: isSelected ? Colors.blue[200] : Colors.white, borderRadius: new BorderRadius.all( const Radius.circular(20.0), ), border: new Border.all( color: isSelected ? Colors.white : Colors.blue[100],//边框颜色 width: 1.0,//边框宽度 ), ), ), new Container( child: new Center( child: isSelected ? new Icon( Icons.check, color: Colors.white, ) : null, ), ), ], ); }}代码地址

Flutter-news
欢迎点赞


推荐阅读
  • Flutter 布局(四) Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth详解
    本文主要介绍Flutter布局中的Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth四种控件,详细介绍了其布局 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了在Ubuntu下制作deb安装包及离线安装包的方法,通过备份/var/cache/apt/archives文件夹中的安装包,并建立包列表及依赖信息文件,添加本地源,更新源列表,可以在没有网络的情况下更新系统。同时提供了命令示例和资源下载链接。 ... [详细]
author-avatar
最爱一片小舟
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有