flutter项目实践01

这篇文章来自我自己的有道云笔记 想看图片去那里
文档:Day 4_1 项目实践.md
链接:http://note.youdao.com/noteshare?id=1b97681ceb71e681d4b41c40ccdf4129&sub=5103CDAE33354B02AB269015C3FD36FB

项目实践

一个小问题

如果你升级了Xcode这些软件 会导致这个运行不出来的问题

这个时候你就需要去 删除这个文件 让它重新加载 这样就可以 让它运行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zYUzejnX-1588835661942)(C18F771E08EF47A4ADEEDBC6A8E9A804)]

进行调试的时候 发现原来的项目 不能跑起来了

大家以后再做类似的事情的时候 你同样会遇到同样的问题

这个东西是不断在升级的 所以 你需要经常去自己处理这种问题

这个问题可以去 flutter的issure上面找到答案

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xX1rfueB-1588835661948)(FF57D4532D254CD38FD8A61AC6807748)]

还有一个问题就是

之前我们使用extension它会有报错 所以我们可以到pubspce.yaml 里面去改变dart语法的最低支持版本

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qN8rgCfR-1588835661952)(697CA235BFF44C67A3BEDF41B848F58B)]

小项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dr4dBKPZ-1588835661958)(F6E91C0350CD416D9D474109A8E9344D)]

新建一个项目肯定是有些东西是要你来完成的

  1. 新建项目 (两种方式创建项目 通过 终端来创建 或者 使用 IDE来创建 [这里推荐使用终端来创建 IDE创建会多很多莫名奇妙的东西 这些东西就 会干扰目录结构])
    • flutter create favorcate(项目名)
  2. 对项目进行配置 (flutter最主要是针对移动端 => App)
    • appid 这个东西是在应用程序在手机里面的唯一标识
    • 应用的名称 默认情况下 项目的名称就是 应用的名称 但是我们最好自己配置
    • 配置对应的图标 icon
    • 启动图 当打开一个项目的时候 需要启动的时间 启动的时候可能就会出现短暂的白屏所以 我们不希望 有这个白屏的情况出现

appid

我们现在先来配置 Androidid

我们来到 Android 这个文件夹 这个东西就是Android的工程目录

我们来修改appid 我们打开这个 build.gradle

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gmz4FSdW-1588835661961)(49FA404B28014A33983C8C50CC85089B)]

可以看到这里有一个applicationId

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWKwxV3u-1588835661964)(00639C7D253242B78708A6E297CFBF97)]

然后我们把它改了就是了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XpvFe0fO-1588835661966)(9703EFF8BB8847CA87829FDF67DC9C5D)]

然后我们发先我们打开这个文件之后它就在报错 主要是说没有找到这样的一个东西

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-th9HUaOE-1588835661968)(2E9B5EC2F6CB4D2C96342C0C490234F4)]

这个东西是 其实是不管它也是可以的 关掉这个文件 他还是在报错 这个时候我们把android Studio重启一下就可以了

应用程序名

但是你发现 这个应用程序名是默认的 项目名 你肯定不希望这个应用程序就是你的项目名

我们可以到 AndroidMainifest.xml文件里面去找对应的东西 这个是非常重要的一个文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dywWsDPk-1588835662001)(AE5662CA64C341D58E69C1D1529C7577)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Y4Hnf6R-1588835662008)(89A8D7F3372C4468915739B7F6150365)]

改掉

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hYTXG4AO-1588835662015)(3756881AA8164005A7126B4D56476985)]

应用程序的图标

我们的应用程序会有一个flutter默认提供的图标

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWQ8kNCV-1588835662021)(57552D49EB274F66B978BC89DD556641)]

但是我们的自己的应用程序肯定不希望用这个图标

我们希望显示美工给我们的图标 那这里怎么办呢

你打开main文件夹 看到里面有很多的文件夹

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJZo5Kfe-1588835662022)(C7B209160D95443698BEE3220CBC1D02)]

这些文件夹就是装的 不同大小的图标的 文件夹

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sSzE9js-1588835662025)(FA372F6F95984B18A2B1ACC319962C04)]

这里有这样的一些东西

把里面的图标换成自己的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfoTvuug-1588835662027)(09C1DE72C0CD4F9BB523D49534697385)]

这个图标的命名也是有讲究的

当然会android开发的就因该很明白

启动图

要搞启动图的话我们来到 layer-list 里面item就是里面的样式 就是白色的

所以我们这里最终呈现出来的就是白色

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IaFUGESm-1588835662029)(F0FB4425E15E4EF9AD03651B424A5A64)]

如果我们要 背景图片的话 我们可以打开下面的注释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MsXezEh-1588835662031)(9DE24D8EA695497D8B1BDB8954DA2687)]

我们可以发现 它也是加载一个mipmap里面的一个 launch_image 的图片

它就会去里面找到对应的

这个地方我们就搞了一张 如果对应分辨率找不到图片它就会到其他的分辨率去找 直到找到为止

所以我们这里可以只用放一张就行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wb9W8icQ-1588835662033)(063C6B641020411499C4C98A118F98F3)]

对应的图片 同样是通过分辨率来进行区别的 这就是Android

IOS 我们就不弄了 因为我用的window 等我有了mac 再做这个ios的东西

如何通过flutter 制作广告

我们可以在进入的页面上搞一个 新的页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-spmi7cku-1588835662035)(FB1E5FAC470D45C2B67F1DB95BE8444A)]

设置一个定时器 然后 时间到了以后就push 进入正真的页面

跳转到另外一个页面 跳转到主要显示的页面就可以了

广告也还是比较简单的

还有一种就是引导页 可以使用pageView来使用

划分目录结构

  1. 新建项目 (两种方式创建项目 通过 终端来创建 或者 使用 IDE来创建 [这里推荐使用终端来创建 IDE创建会多很多莫名奇妙的东西 这些东西就 会干扰目录结构])
    • flutter create favorcate(项目名)
  2. 对项目进行配置 (flutter最主要是针对移动端 => App)
    • appid 这个东西是在应用程序在手机里面的唯一标识
    • 应用的名称 默认情况下 项目的名称就是 应用的名称 但是我们最好自己配置
    • 配置对应的图标 icon
    • 启动图 当打开一个项目的时候 需要启动的时间 启动的时候可能就会出现短暂的白屏所以 我们不希望 有这个白屏的情况出现
  3. 划分目录结构
    • pages 页面的文件
    • widgets 封装的组件
    • network/services 网络请求
    • router 路由
    • viewmodel viewmodel MVVM文件
    • model 模型
    • shared 共享的文件

所以我们就把这些目录结构放到 我们的目录结构里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3cR8PSkt-1588835662037)(F0C634B700584F2BAB189C0E28C4176E)]

但是这些也不是最终的目录结构 我们最终会生成很多的文件夹 这样的lib下面就会有很多的文件夹

这个时候全部都去lib里面去找其实也不是特别方便的 所以这里就进行了另外一层考虑

我们可以把目录结构划分的深一点 这样就可以说我们的目录结构 划分的比较好

我们会先搞两个文件

  • core
    • services/network
    • router
    • viewmodel 不是 视图相关的东西都放到core里面
    • model
  • ui
    • pages…

但是因为android Studio是开发Android的东西 它直接建文件夹认为你建的是包 这个时候看文件夹层次就不太好看 所以我们 直接在资源管理器里面把文件夹建好

这样我们功能相关的东西都放在上面 和页面相关的东西都放在下面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nzMWC63A-1588835662039)(5A8AA7EAA7354EFCB12A6EF5438B392C)]

主题相关的东西

这样划分的话就是这样的

  1. 新建项目 (两种方式创建项目 通过 终端来创建 或者 使用 IDE来创建 [这里推荐使用终端来创建 IDE创建会多很多莫名奇妙的东西 这些东西就 会干扰目录结构])
    • flutter create favorcate(项目名)
  2. 对项目进行配置 (flutter最主要是针对移动端 => App)
    • appid 这个东西是在应用程序在手机里面的唯一标识
    • 应用的名称 默认情况下 项目的名称就是 应用的名称 但是我们最好自己配置
    • 配置对应的图标 icon
    • 启动图 当打开一个项目的时候 需要启动的时间 启动的时候可能就会出现短暂的白屏所以 我们不希望 有这个白屏的情况出现
  3. 划分目录结构
    • pages 页面的文件
    • widgets 封装的组件
    • network/services 网络请求
    • router 路由
    • viewmodel viewmodel MVVM文件
    • model 模型
    • shared 共享的文件
  4. 主题相关的东西

main.dart的文件因该尽量简单

把项目中默认的生成的计数器案例给换了

甚至我们的Scaffold里面的home我们都不是不需要的 我们可以用initialRoute来代替

import "package:flutter/material.dart";

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent
      ),
      initialRoute: ,
    );
  }
}

  • 设置共有的背景

然后我们就先搞定 Theme这个东西

这里我们直接把之前封装好的 app_theme.dart 文件拿过来

当然这个我们就主要以普通模式作为开发目标了

我们发现每个页面都有一个相同的背景颜色

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5qQqyKta-1588835662041)(0642E1AFCC1D4F07B9A9ACE9BF2520E0)]

同样这个背景颜色也是可以在Theme里面设置的

使用canvasColor进行设置

sharded/app_theme.dart

import "package:flutter/material.dart";

class HYAppTheme {
//  1. 公共属性
  static const double smallFontSize = 16;
  static const double normalFontSize = 20;
  static const double largeFontSize = 24;

//  2. 普通模式
  static final Color normalTextColors = Colors.red;

//  可以在这里把它封装成一个方法 也可以封装成一个属性
  static final ThemeData norTheme = ThemeData(
      primarySwatch: Colors.pink, // primaryColor accentColor 如果不一样再单独设置
      canvasColor: Color.fromRGBO(255, 254, 222, 1),
      textTheme: TextTheme(
          body1: TextStyle(fontSize: normalFontSize, color: normalTextColors)
      )
  );

//  3. 暗黑模式
  static final Color darkTextColors = Colors.green;

  static final ThemeData darkTheme = ThemeData(
      primarySwatch: Colors.grey,
      textTheme: TextTheme(
          body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
      )
  );
}

main.dart

import "package:flutter/material.dart";
import 'package:project03/ui/shared/app_theme.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "美食广场",
      theme: HYAppTheme.norTheme
    );
  }
}

  • 设置textTheme

还有一个就是关于我们的textTheme

我们知道textTheme里面有很多的属性

body1 默认的字体样式

body2

dispaly1

display2

display3

display4

当然我们希望能有一个共有默认字体大小 不想要的时候覆盖掉就行了

这里我们就不改它了

我们通过常量来对这些东西做一个规划(这样的好处是 不用在里面的繁杂代码里面去到处找颜色的设置)

import "package:flutter/material.dart";

class HYAppTheme {
//  1. 公共属性
  static const double bodyFontSize = 14;
  static const double smallFontSize = 16;
  static const double normalFontSize = 20;
  static const double largeFontSize = 24;

//  2. 普通模式
  static final Color normalTextColors = Colors.red;

//  可以在这里把它封装成一个方法 也可以封装成一个属性
  static final ThemeData norTheme = ThemeData(
      primarySwatch: Colors.pink, // primaryColor accentColor 如果不一样再单独设置
      canvasColor: Color.fromRGBO(255, 254, 222, 1),
      textTheme: TextTheme(
          body1: TextStyle(fontSize: bodyFontSize),
          display1: TextStyle(fontSize: smallFontSize),
          display2: TextStyle(fontSize: normalFontSize),
          display3: TextStyle(fontSize: largeFontSize),
      )
  );

//  3. 暗黑模式
  static final Color darkTextColors = Colors.green;

  static final ThemeData darkTheme = ThemeData(
      primarySwatch: Colors.grey,
      textTheme: TextTheme(
          body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
      )
  );
}

这个就是关于我们字体的一个东西

还有一个就是字体的颜色

这里主题就搞的差不多了

运行代码

但是现在还是差点 我们Scaffold里面至少 home和initialRoute因该要有一个

但我们两个都没有所以我们的项目肯定是不能跑的

配置路由

router/router.dart

import 'package:flutter/material.dart';


class HYRouter {
  static final String initialRoute = "/";

  static final Map routes ={
  };

  static final RouteFactory generateRoute = (settings) {
  };

  static final RouteFactory unknownRoute = (setting) {
  };
}

我们默认要启动的页面就是 大的页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZyGy3nPF-1588835662044)(F78015FAB5B04CA68CF992F86D6981ED)]

这个页面我们之前说douban 的时候就是一个main 它是一个主要的页面

同样把文件结构先搞出来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-km0gxnme-1588835662046)(4618A9232BA848F49ABBDDAFAAB85C35)]

我们建立的这个main因该是一个有状态的 东西

所以我们使用StatefulWidget

构建整个类

import "package:flutter/material.dart";

class HYMainScreen extends StatefulWidget {
  static const String routeName = "/";

  @override
  _HYMainScreenState createState() => _HYMainScreenState();
}

class _HYMainScreenState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(

      ),
      bottomNavigationBar: BottomNavigationBar(
        items: BottomNavigationBarItem(

        ),
      ),
    );
  }
}

然后设置对应的 路由相关的东西

  • Day3_25 手势(事件监听), 事件总线 和 路由和导航 3344行

说命令 这个onGenerateRoute是怎么设置的

同样也可以 有错误页面的设置 unKownRoute怎么设置的

import 'package:flutter/material.dart';
import 'package:project03/ui/pages/main/main.dart';


class HYRouter {
  static final String initialRoute = HYMainScreen.routeName;

  static final Map routes ={
    HYMainScreen.routeName: (ctx) => HYMainScreen()
  };

//  自己扩展
  static final RouteFactory generateRoute = (settings) {
    return null;
  };

  static final RouteFactory unknownRoute = (setting) {
  };
}

因为我们这几个东西都没有写

这样我们的主题和路由相关的东西就配好了

你会发现我们的MaterialApp里面是非常简单的

淡然还有一种可以把MyApp整个东西抽成一个文件 这样你就有一个文件可以专门来管理你的整个app了

但是我不喜欢这样所以不抽 这里的代码本来就已经比较复杂了

其实更不希望在lib的下面有更多的文件 我们希望我们的lib目录尽可能的简单

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m2tA6jUY-1588835662048)(780DA7A7771647929E16082FDDCEC350)]

所以我们比较希望这样搞

import "package:flutter/material.dart";
import 'package:project03/core/router/router.dart';
import 'package:project03/ui/shared/app_theme.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "美食广场",
//      主题
      theme: HYAppTheme.norTheme,

//      路由
      initialRoute: HYRouter.initialRoute,
      routes: HYRouter.routes,
      onGenerateRoute: HYRouter.generateRoute,
      onUnknownRoute: HYRouter.unknownRoute,
    );
  }
}

Main页面的制作

  1. 新建项目 (两种方式创建项目 通过 终端来创建 或者 使用 IDE来创建 [这里推荐使用终端来创建 IDE创建会多很多莫名奇妙的东西 这些东西就 会干扰目录结构])
    • flutter create favorcate(项目名)
  2. 对项目进行配置 (flutter最主要是针对移动端 => App)
    • appid 这个东西是在应用程序在手机里面的唯一标识
    • 应用的名称 默认情况下 项目的名称就是 应用的名称 但是我们最好自己配置
    • 配置对应的图标 icon
    • 启动图 当打开一个项目的时候 需要启动的时间 启动的时候可能就会出现短暂的白屏所以 我们不希望 有这个白屏的情况出现
  3. 划分目录结构
    • pages 页面的文件
    • widgets 封装的组件
    • network/services 网络请求
    • router 路由
    • viewmodel viewmodel MVVM文件
    • model 模型
    • shared 共享的文件
  4. 主题相关的东西
  5. 路由配置
  6. HYMainScreen

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-krxn5mZf-1588835662050)(D32B65629E18422980E125303EA1B3C7)]

最主要的就是这几个页面的对应

我们这里的BottonNavigationBar 我们可以将它分到其他的地方

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNwb6vC0-1588835662052)(37E74913556E4CDE86EDB88DFC68FD67)]

但是 一般感觉 这个地方代码只有这么长 我们一般感觉是没有必要 专门分一个文件吧

但是实际上我们开始 开发了之后 这个 文件容易

我们可以把它划分的精细一点 做的事情很单一 这样我们后期就不用代码重构 这样可以省下很多的整理代码的功夫

那具体要做某些事情的时候我们就找到某些文件 直接在文件中操作就会比较容易

import "package:flutter/material.dart";

final List pages = [];

final List items = [];

这些里面我们不希望再对它进行赋值的操作 所以加上

import "package:flutter/material.dart";

final List pages = [];

final List items = [
  BottomNavigationBarItem(
    title: Text("首页"),
    icon: Icon(Icons.home)
  ),
  BottomNavigationBarItem(
    title: Text("收藏"),
    icon: Icon(Icons.home)
  )
];

然后我们创建两个页面

home.dart

import "package:flutter/material.dart";

class HYHomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("美食广场")
      ),
      body: Center(child: Text("美食广场"))
    );
  }
}

favor.dart

import "package:flutter/material.dart";

class HYFavorScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("favor")
        ),
        body: Center(child: Text("favor"))
    );
  }
}

main.dart

import "package:flutter/material.dart";
import "initialize_items.dart";

class HYMainScreen extends StatefulWidget {
  static const String routeName = "/";

  @override
  _HYMainScreenState createState() => _HYMainScreenState();
}

class _HYMainScreenState extends State {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: items,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZYn0bIzb-1588835662054)(5CFE2D7739E5436EB8C6A8846342A083)]

但是这个字体可能会一直变化

import "package:flutter/material.dart";
import "initialize_items.dart";

class HYMainScreen extends StatefulWidget {
  static const String routeName = "/";

  @override
  _HYMainScreenState createState() => _HYMainScreenState();
}

class _HYMainScreenState extends State {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: items,
        selectedFontSize: 14,
        unselectedFontSize: 14,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}

制作首页

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nS9q0X4v-1588835662057)(9924F5D562BF4B92B24A118F59CE2B47)]

这里有很多的数据 这些 页面数据我们就不通过网络请求来获得了 我们做成一个json文件来做json的解析

这里要补充一点json文件的解析

然后这个是一个资源 那我们就要对它进行配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8Kzcki6-1588835662060)(4CE5C8E025CE4A0DB25C56553F1A2220)]

我们来到这个配置文件 注意这个前面有一个空格要删掉

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1xnVVXX-1588835662150)(060C09FF670443B4A1CDD25737F7BDBE)]

这样才对

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3IdSM43-1588835662151)(C130F715EB4F4961A93847A8844D525B)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yr3KKtii-1588835662153)(1544579FDF2348AC817CA10C3427C386)]

改成这样就可以了

同样注意 我们也要 执行pub get 命令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fZxrxc1-1588835662155)(A5DD96F230FC42ED96560CC3F474D21B)]

然后我们就要读取我们的json文件

加载完 之后就可以对json做一个解析

那我们在哪里进行加载呢 可以在build里面做吗 最好不要 这个里面 会经常执行 那么就

意味这我们要经常执行 请求 所以这个东西 我们就最好不要搞在这个build里面

  • 所以这里可以用stf 来完成对应的操作
  • 这里我们也可以 分一个文件出去来完成这个操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXJ3LmWL-1588835662157)(EF1F1358769546F9B71E01010C93E424)]

import "package:flutter/material.dart";

class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

  • 那么这个地方怎么来考虑 我怎么知道 这样要分开 成文件呢 为什么不直接把 这个HYHomeScreen 变成 StatusfulWidget

如果 在homeScreen 有很多的地方要用 这个数据我们就可以这样使用 这个 如果我们的floatActionButton 也需要这个东西那我们 就需要将 这个HYHomeScreen 也变成stf了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T8wXLluO-1588835662159)(6C7423EA03434F6CAF4EA3E62E4FA227)]

这里我要做json请求那我们是直接请求吗 肯定是在initStatus里面进行请求的

import "package:flutter/material.dart";

class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {

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

//    加载数据
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

我们先到 services里面先去做一个处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1LJSQV5P-1588835662162)(58E7BE16C702478191BC5F908C657C92)]

我们用rootBundle 来进行处理

import 'dart:convert';

import 'package:flutter/services.dart';

class JsonParse {
  static void getCategoryData() async {
//    1. 加载json文件
//  它这里最终是返回了一个字符串 从字符串里面解析肯定是不好解析的
    final jsonString = await rootBundle.loadString("assets/json/category.json");

//    2. 将我们的jsonString转化成Map/List
    final result = json.decode(jsonString);
  }
}
  • 这里json.decode 是对json字符串的一个解构 它可以将字符串里面的所有 数组做成List 将里面的所有 对象做成 map

我们这里就拿到一个map了 我们这里最外面其实是一个对象 所以我们拿到的是一个map

当然转化成 map还不够 还要将里面的东西全部转化成模型

这个和我们之前讲的东西

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4G7sCJ0x-1588835662164)(91BC7FC374C441F49A3555C1063E1ADE)]

我们用 这个

  • https://javiercbk.github.io/json_to_dart/
  • https://app.quicktype.io/ 这两个都可以 但是下面这个功能要强一点

来做 json转话成模型的快速生成代码

    {
      "id": "c1",
      "title": "意大利",
      "color": "9C27B0"
    }

取个名字 就可以了 HYCategoryModel

  • HY是为了和系统类做区分
  • Model是为了避免以后创建HYCategory的类 做区分 这个是模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8x0my2n0-1588835662167)(2E28B9A4C4684B6C91DB857B49791C35)]

这里 其实就是一个map转 对象的过程

import 'dart:convert';

import 'package:flutter/services.dart';
import 'package:project03/core/model/category_model.dart';

class JsonParse {
  static Future> getCategoryData() async {
//    1. 加载json文件
//  它这里最终是返回了一个字符串 从字符串里面解析肯定是不好解析的
    final jsonString = await rootBundle.loadString("assets/json/category.json");

//    2. 将我们的jsonString转化成Map/List
    final result = json.decode(jsonString);

//    3.  将Map中的内容转换成一个个对象
    final resultList = result["category"];
//    我们可以一个一个转化成 模型
    List categories = [];
    for(var json in resultList ) {
      categories.add(HYCategoryModel.fromJson(json));
    }

    return categories;
  }
}

使用

import "package:flutter/material.dart";
import 'package:project03/core/services/json_parse.dart';

class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {

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

//    加载数据
    JsonParse.getCategoryData().then((res) {
      print(res);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

成功加载出数据了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fsqEoT8I-1588835662169)(2AC94566D671472B90559B51A8F61521)]

取到数据之后就是展示

import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';

class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {

  List _categories = [];

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

//    加载数据
    JsonParse.getCategoryData().then((res) {
      _categories = res;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        gridDelegate: null,
        itemBuilder: null
    );
  }
}

这个长度要给出来

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        itemCount: _categories.length,
        gridDelegate: null,
        itemBuilder: null
    );
  }

不给出来的话 默认是无限

展示 我们用GridView

  • GridView的使用 详细讲解 Day3_11 常见的滚动的Widget
import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';

import "../../../core/extension/int_extension.dart";


class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {

  List _categories = [];

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

//    加载数据
    JsonParse.getCategoryData().then((res) {
      _categories = res;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        itemCount: _categories.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 20.px,
          mainAxisSpacing: 20.px,
          childAspectRatio: 1.5
        ),
        itemBuilder: (ctx, index) {
          return Text(_categories[index].title);
        }
    );
  }
}

同样我们这里单位用了 px 所以extension走起 Sizefit注意要初始化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qls6wj9P-1588835662170)(43C1E66DC3DF4300891DA92CCE2EA074)]

文件结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZhgRZwJ-1588835662172)(A431A477019B48E683200FC6574DD5B9)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EC3dvukX-1588835662174)(CA2C170DC1DA4AEF90ECA263F97D073D)]

这样我们就可以了

  • 两边离的太近了 padding
  • 还是不好看 居中 alignment: Alignment.center
import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';

import "../../../core/extension/int_extension.dart";


class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {

  List _categories = [];

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

//    加载数据
    JsonParse.getCategoryData().then((res) {
      _categories = res;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        padding: EdgeInsets.all(20.px),
        itemCount: _categories.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 20.px,
          mainAxisSpacing: 20.px,
          childAspectRatio: 1.5
        ),
        itemBuilder: (ctx, index) {
          return Container(
            color: Colors.red,
            alignment: Alignment.center,
            child: Text(_categories[index].title)
          );
        }
    );
  }
}

但是还是想要一个圆角

注意decoration 这个东西和 color冲突所以 将color放到里面

 @override
  Widget build(BuildContext context) {
    return GridView.builder(
        padding: EdgeInsets.all(20.px),
        itemCount: _categories.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 20.px,
          mainAxisSpacing: 20.px,
          childAspectRatio: 1.5
        ),
        itemBuilder: (ctx, index) {
          return Container(
            decoration: BoxDecoration(
              color: Colors.red,
              borderRadius: BorderRadius.circular(12.px)
            ),
            alignment: Alignment.center,
            child: Text(_categories[index].title)
          );
        }
    );
  }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GoE74YMC-1588835662176)(B17D283F2FA84001A7CAA58E2921399E)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MhBAStYK-1588835662178)(52F97971EC04402AB28C395DD7E1FEF2)]

看得出 这个东西是有一个渐变色的 颜色是不一样的

这个颜色是取决与哪里呢 很明显它不是一个随机的颜色

这个颜色取决于 我们的json的文件 那我们就需要将 这个转化成 颜色

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-liBZL2P6-1588835662180)(1BA37C8DB05149E484A23E4EFF7C77EA)]

如果用原来的 fromRGBO的方式 它还要做字符串截取

我们可以把它转化成 16进制

这个时候我们可以到 model里面去做处理

  1. 将我们的字符串Color转换成16进制的数字
  2. 将透明度加进去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQQ1h5gK-1588835662182)(39D5DCD6BEA645A08C90DA120835DFF7)]

import "package:flutter/material.dart";

class HYCategoryModel {
  String id;
  String title;
  String color;
  Color cColor;

  HYCategoryModel({this.id, this.title, this.color});

  HYCategoryModel.fromJson(Map json) {
    id = json['id'];
    title = json['title'];
    color = json['color'];

//    1. 将我们的字符串Color转换成16进制的数字
    final colorInt = int.parse(color, radix: 16);
//    2. 将透明度加进去
//  我们这里可以用一下 for运算符
    cColor = Color(colorInt | 0xFF000000);
  }

  Map toJson() {
    final Map data = new Map();
    data['id'] = this.id;
    data['title'] = this.title;
    data['color'] = this.color;
    return data;
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEE9ewG1-1588835662185)(C17F47B8ED3F447CAF16C8D3137787A5)]

这样就可以了

然后我们这里还要做一个颜色的渐变

我们做渐变使用这个 LinearGradient Widgets

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        padding: EdgeInsets.all(20.px),
        itemCount: _categories.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 20.px,
            mainAxisSpacing: 20.px,
            childAspectRatio: 1.5
        ),
        itemBuilder: (ctx, index) {
          final bgColor = _categories[index].cColor;

          return Container(
              decoration: BoxDecoration(
                  color: bgColor,
                  borderRadius: BorderRadius.circular(12.px),
                  gradient: LinearGradient(
                    colors: [
                      bgColor.withOpacity(.5),
                      bgColor
                    ]
                  )
              ),
              alignment: Alignment.center,
              child: Text(_categories[index].title)
          );
        }
    );
  }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lOaK4A2H-1588835662187)(F7A342C0FE3445C899EA9C07F8841510)]

微调一下样式

import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';

import "../../../core/extension/int_extension.dart";


class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State {

  List _categories = [];

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

//    加载数据
    JsonParse.getCategoryData().then((res) {
      _categories = res;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        padding: EdgeInsets.all(20.px),
        itemCount: _categories.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 20.px,
            mainAxisSpacing: 20.px,
            childAspectRatio: 1.5
        ),
        itemBuilder: (ctx, index) {
          final bgColor = _categories[index].cColor;

          return Container(
              decoration: BoxDecoration(
                  color: bgColor,
                  borderRadius: BorderRadius.circular(12.px),
                  gradient: LinearGradient(
                    colors: [
                      bgColor.withOpacity(.5),
                      bgColor
                    ]
                  )
              ),
              alignment: Alignment.center,
              child: Text(
                  _categories[index].title,
                style: Theme.of(context).textTheme.display2.copyWith(
                  fontWeight: FontWeight.bold
                )
              )
          );
        }
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eA6swYaL-1588835662189)(1763215519EC4D4AA3383903D1B4567B)]

主题也重写一下 字体不好看

import "package:flutter/material.dart";

class HYAppTheme {
//  1. 公共属性
  static const double bodyFontSize = 14;
  static const double smallFontSize = 16;
  static const double normalFontSize = 20;
  static const double largeFontSize = 24;

//  2. 普通模式
  static final Color normalTextColors = Colors.red;

//  可以在这里把它封装成一个方法 也可以封装成一个属性
  static final ThemeData norTheme = ThemeData(
      primarySwatch: Colors.pink, // primaryColor accentColor 如果不一样再单独设置
      canvasColor: Color.fromRGBO(255, 254, 222, 1),
      textTheme: TextTheme(
          body1: TextStyle(fontSize: bodyFontSize),
          display1: TextStyle(fontSize: smallFontSize, color: Colors.black87),
          display2: TextStyle(fontSize: normalFontSize, color: Colors.black87),
          display3: TextStyle(fontSize: largeFontSize, color: Colors.black87),
      )
  );

//  3. 暗黑模式
  static final Color darkTextColors = Colors.green;

  static final ThemeData darkTheme = ThemeData(
      primarySwatch: Colors.grey,
      textTheme: TextTheme(
          body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
      )
  );
}

当主题改动的时候 我们要 hot restart

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpjBTRiS-1588835662191)(440DD4035ED048EBB83A15203A9CD5AD)]

你可能感兴趣的:(flutter)