基于flutter框架实现的一款简单的应用

基于flutter框架实现的一款简单的应用

这是我在学flutter框架的时候自己写的一个入门级的应用。其中实现了组件的基本使用,基本的网络请求以及json的使用(本地json以及网络接口的调用)等,在下面的文章中,将会介绍这个应用怎样进行界面的设计,怎样实现对应的功能。


应用截图

实现的功能:图片浏览,新闻浏览等常用功能,截图如下:
图片浏览
在这里插入图片描述
** 因为我喜欢摄影,所以加上了拍摄技巧,有兴趣的朋友可以自我发挥


一、项目的基本架构

在这里插入图片描述

二、具体实现

1、主界面实现

main.dart:

import 'dart:io';
import 'package:built_value/built_value.dart';
import 'package:flutter/material.dart';
import 'package:pictureshow/page/PhotoPage.dart';
import 'package:pictureshow/page/SkillPage.dart';
import 'package:flutter/services.dart';
import 'package:pictureshow/page/AboutPage.dart';
import 'package:pictureshow/page/NewsPage.dart';


void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  //静态_title字符串
  static const String _title = '图片展';

  //组件构建器
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(  ///MaterialApp风格
      debugShowCheckedModeBanner: false,
      title: _title,
      home: MainStatefulWidget(),  ///用MainStatefulWidget()内部类_MainStatefulWidgetState实现home
      theme: ThemeData(  ///主题风格颜色
        primaryColor: Colors.green
      ),
      );
  }
}

//类似于一个接口类,StatefulWidget组件
class MainStatefulWidget extends StatefulWidget {
  @override
  _MainStatefulWidgetState createState() => _MainStatefulWidgetState();
}

/*
类似于一个实现类,继承MainStatefulWidget,而MainStatefulWidget又继承于StatefulWidget
所以MainStatefulWidget继承了StatefulWidget有状态的属性
*/
//持久化
class _MainStatefulWidgetState extends State<MainStatefulWidget> with AutomaticKeepAliveClientMixin<MainStatefulWidget>,SingleTickerProviderStateMixin{

  /*保证页面的活跃*/
  @override
  bool get wantKeepAlive => true; ///see AutomaticKeepAliveClientMixin


  int CurrentIndex = 0;  ///当前设定底部导航栏的索引
  List<StatefulWidget> pageList = [
      PhotoPage(),
      SkillPage(),
      NewsPage(),
  ];///pageList用来存每个导航栏的组件所对应的页面
  @override
  //继承StatefulWidget实现组件构建,具体方法
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("优选美图"),
        actions: [
          PopupMenuButton<String>(
            onSelected: (value){   ///当选中时
              if(value == 'exit') ///做一个判断,如果返回的value为exit,实现退出程序
                SystemNavigator.pop();
              else               //这样的话下一个按钮就是跳转到“关于”页面
                Navigator.push(context,new MaterialPageRoute(builder: (context) => new AboutPage())); ///利用路由跳转
              setState(() {   ///设置状态,需要传值的话可以设置

              });
            },
            itemBuilder: (context) {
              return [
                PopupMenuItem<String>(
                  value: 'about',
                  child: Text('关于'),
                ),
                PopupMenuItem<String>(
                  value: 'exit',
                  child: Text('退出'),
                ),
              ];
            },
          )
        ],///动作
      ),
      body: this.pageList[this.CurrentIndex], ///主体
      bottomNavigationBar: BottomNavigationBar(
        //底部导航栏的每一个元素
        items:<BottomNavigationBarItem> [
          BottomNavigationBarItem(icon: Icon(Icons.photo),title: Text("美图欣赏")),
          BottomNavigationBarItem(icon: Icon(Icons.photo_camera),title: Text("拍摄技巧")),
          BottomNavigationBarItem(icon:Icon(Icons.new_releases),title:Text("每日新闻")),
        ],
        currentIndex: this.CurrentIndex,///当前索引,指导航栏指向第几个元素
        onTap: (index){                      ///onTap点击事件,参数是index索引
          setState(() {                       ///设置状态,当被点击之时会产生界面刷新的效果
            this.CurrentIndex = index;       ///将当前index赋值给CurrentIndex  由此改变body指向的界面
          });
        },
        type: BottomNavigationBarType.fixed,  ///显示模式,fixed效果
        backgroundColor: Colors.white,        ///导航栏背景颜色,默认为白色
      ),
    );
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }
}

NewsPage.dart:

class NewsPage extends StatefulWidget {
  @override
  _NewsPageState createState() => _NewsPageState();
}
var _getnews;///暂存 从接口获取到的数据

class _NewsPageState extends State<NewsPage>{
  @override
  Widget build(BuildContext context) {
    return  Center(       ///中心布局
          child: FutureBuilder(
            ///future异步处理
            future: _getNews(),
            builder: (BuildContext context, AsyncSnapshot<Response> snapshot) {
              /*表示数据成功返回*/
              if (snapshot.hasData) {
                Response response = snapshot.data;
                var mydata = json.decode(response.toString());
                return RefreshIndicator(    ///下拉刷新组件
                    color: Colors.green,   ///圆圈进度颜色
                    displacement: 44.0,   ///下拉停止距离
                    backgroundColor: Colors.grey[200],  ///背景颜色
                    child: ListView.separated(itemCount: mydata == null ? 0 : mydata["result"]["data"].length,  ///判断长度
                      itemBuilder: (BuildContext context, int index) {
                      return new ListTile(
                        title: Row(
                        children: <Widget>[
                          Expanded(child:
                          Text(mydata["result"]["data"][index]["title"],
                            softWrap: true,
                            overflow: TextOverflow.ellipsis,
                            maxLines: 2,)
                          ),
                        ],
                      ),

                      ///点击时的事件
                      onTap: () async {
                        ///路由传参
                        String result = await Navigator.push(
                            context, new MaterialPageRoute(builder: (context) => new NewsPageDetailPage(
                            mydata["result"]["data"][index]["url"])
                        ));
                      },
                    );
                  },
                  separatorBuilder: (BuildContext context, int index){
                      return Divider();          ///分割线
                  },
                ),
                    ///实现下拉刷新
                    onRefresh:() async{
                        if(_getnews != null) {
                          _getnews = null;
                          await _getNews();
                        }
                    });
              }else{
                return CircularProgressIndicator();
              }
            },
          ),
        );
      }

}

/**
 * 请求接口获取数据
 */
Future<Response> _getNews() async {
  ///如果缓存的_getnews 为空,则进行数据请求
  if(_getnews == null){
    await Future.delayed(Duration(seconds: 1), () {
      print("延时1秒后请求数据");
    });

    String url = "http://v.juhe.cn/toutiao/index";
    String key = "4c52313fc9247e5b4176aed5ddd56ad7";
    String type = "keji";

    print("开始请求数据");
    Response response = await Dio().get(url, queryParameters: {"type": type, "key": key});
    print("请求完成");
    _getnews = response;
    return response;
  }else  ///如果缓存的_getnews 不为空,则说明在绘制的时候就已经加载完了,就不需要再请求(太耗流量),则将之前存的_getnews返回给future
    {
    return _getnews;
  }
}

PhotoPage.dart:

class PhotoPage extends StatefulWidget {
  @override
  _PhotoPageState createState() => _PhotoPageState();
}


class _PhotoPageState extends State<PhotoPage> {
  List data;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(               //Center 中心定位控件
        child: FutureBuilder(        //FutureBuilder  展示异步任务状态
          future: DefaultAssetBundle.of(context).loadString('json/loadJson/photoJson.json'),//加载路径
          builder: (context,snapshot){  //builder两个参数 上下文,映射
            var mydata = json.decode(snapshot.data.toString());  //json_decode — 对JSON 格式的字符串进行解码
            if(mydata==null){
              return CircularProgressIndicator();
            }else{
              return ListView.builder(itemBuilder: (BuildContext context,int index){
                return Card(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children:<Widget> [
                      Image.network(mydata[index]["photo_src"],width: 400,height: 300,),
//                    Image.asset("",width: 400,height: 300,), //导入图片
//                    Text("链接:"+mydata[index]["photo_src"]),
                      Text("名称:"+mydata[index]["photo_name"],textScaleFactor: 1.2,),
                      Text("类型:"+mydata[index]["photo_type"],textScaleFactor: 1.2,),
                      Text("日期:"+mydata[index]["photo_date"],textScaleFactor: 1.2,),
                    ],
                  ),
                );
              },
                itemCount: mydata == null ? 0 : mydata.length,  //判断元素长度
              );;
            }
          },
        ),
      ),
    );
  }
}

SkillPage.dart:

class SkillPage extends StatefulWidget {
  @override
   _SkillPageState createState() =>  _SkillPageState();
}

class _SkillPageState extends State<SkillPage> {
  List data;
  var _mydata;
  int _index;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(               //Center 中心定位控件
        child: FutureBuilder(        //FutureBuilder  展示异步任务状态
          future: DefaultAssetBundle.of(context).loadString('json/loadJson/SkillJson.json'),//加载路径
          builder: (context,snapshot){  ///builder两个参数 上下文,快照
            var mydata = json.decode(snapshot.data.toString());  ///json_decode — 对JSON 格式的字符串进行解码
            if(mydata==null){
              return CircularProgressIndicator();
            }else{
              return ListView.separated(
                itemCount: mydata == null ? 0 : mydata.length,  ///判断长度
                itemBuilder: (BuildContext context, int index) {
                    return new ListTile(
                        title: Row(
                          children: <Widget>[
                            Icon(Icons.circle,
                            size: 10.0,),
                            SizedBox(width: 10,),
                            Expanded(child:
                              Text(mydata[index]["skill_name"],
                              softWrap: true,
                              overflow: TextOverflow.ellipsis,
                              maxLines: 1,
                              textScaleFactor: 1.0,
                            ),)
                          ],
                        ),
                        onTap: () async {
                          ///路由传参
                          String result = await Navigator.push(
                              context, new MaterialPageRoute(builder: (context) => new SkillTwoPage(
                              mydata[index]["skill_name"],
                              mydata[index]["skill_date"],
                              mydata[index]["skill_thing"])
                          ));
                        },
                      );
                },
                separatorBuilder: (BuildContext context, int index){
                  return Divider();          ///分割线
                },
              );
            }
          },
        ),
      ),
    );
  }
}

2、Json实体类实现

photo_to_entity.dart:

class PhotoToEntity {
  String _photoName;
  String _photoType;
  String _photoDate;
  String _photoSrc;

  String get photoName => _photoName;
  String get photoType => _photoType;
  String get photoDate => _photoDate;
  String get photoSrc => _photoSrc;

  PhotoToEntity({
      String photoName, 
      String photoType, 
      String photoDate, 
      String photoSrc}){
    _photoName = photoName;
    _photoType = photoType;
    _photoDate = photoDate;
    _photoSrc = photoSrc;
}

  PhotoToEntity.fromJson(dynamic json) {
    _photoName = json["photo_name"];
    _photoType = json["photo_type"];
    _photoDate = json["photo_date"];
    _photoSrc = json["photo_src"];
  }

  Map<String, dynamic> toJson() {
    var map = <String, dynamic>{};
    map["photo_name"] = _photoName;
    map["photo_type"] = _photoType;
    map["photo_date"] = _photoDate;
    map["photo_src"] = _photoSrc;
    return map;
  }

}

skill_to_entity.dart:

class Skill_to_entry {
  String _skillName;
  String _skillDate;
  String _skillThing;

  String get skillName => _skillName;
  String get skillDate => _skillDate;
  String get skillThing => _skillThing;

  Skill_to_entry({
      String skillName, 
      String skillDate, 
      String skillThing}){
    _skillName = skillName;
    _skillDate = skillDate;
    _skillThing = skillThing;
}

  Skill_to_entry.fromJson(dynamic json) {
    _skillName = json["skill_name"];
    _skillDate = json["skill_date"];
    _skillThing = json["skill_thing"];
  }

  Map<String, dynamic> toJson() {
    var map = <String, dynamic>{};
    map["skill_name"] = _skillName;
    map["skill_date"] = _skillDate;
    map["skill_thing"] = _skillThing;
    return map;
  }

}

3、界面详细类实现

NewsPageDetailPage.dart:

class NewsPageDetailPage extends StatelessWidget {
  ///构造
  String _news_url;
  NewsPageDetailPage(this._news_url);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("每日新闻"),
        ),
        body: Center(
            child: WebView(
              initialUrl: this._news_url,
            )
        )
    );
  }
}

SkillTwoPage.dart:

class SkillTwoPage extends StatelessWidget {
  @override
  String _skill_name;
  String _skill_date;
  String _skill_thing;
  ///构造
  SkillTwoPage(this._skill_name,this._skill_date,this._skill_thing);
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(this._skill_name),
      ),
      body: Container(
        child: ListView(
          children: [
            Column(
              children: <Widget>[
                Text(this._skill_thing,
                  textScaleFactor: 1.2,)
              ],
            ),
          ],
        )
      ),
    );
  }
}

4、配置文件

pubspec.yaml配置文件主要部分

dependencies:
  dio: ^3.0.5
  flutter:
    sdk: flutter
  json_annotation: ^4.0.1
  built_value: ^8.0.4
  fluttertoast2: ^0.0.8
  http: ^0.12.2
  async: ^2.5.0
  webview_flutter: ^0.3.9+1

  cupertino_icons: ^1.0.2
  english_words: ^3.1.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.0.0
  json_serializable: ^4.1.0
  
fonts:
    - family: myGoodIcon
      fonts:
        - asset: fonts/goodIcon.ttf
        - asset: fonts/menuIcon.ttf

assets:
      - assets/image/AppIcon .png
      - json/loadJson/photoJson.json
      - json/loadJson/SkillJson.json

network_security_config.xml网络请求配置文件主要部分

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值