Flutter 商城App开发指南 | Flutter 学习笔记

Flutter是Google开源的构建用户界面(UI)工具包,帮助开发者通过一套代码库高效构建多平台精美应用。

文章描述我在学习 Flutter 时仿 贪吃商城 开发 Demo 的全过程。旨在帮助有布局基础的 flutter 新手完整地开发一个移动端应用

商城Demo 展示图: 11981650349242_.pic.jpg

阅读文章你将收获

  • Flutter 快速上手能力
  • 开发中部分问题的解决方案
  • Flutter 数据模型的编写
  • 集成 iconfont 字体图标
  • 打包 安卓APP 注意事项与具体流程

Flutter 开发环境搭建

项目使用 Flutter 2.10.2 版本,您可使用相近的 Flutter 2.x 版本:

Flutter 2.10.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 097d3313d8 (8 weeks ago) • 2022-02-18 19:33:08 -0600
Engine • revision a83ed0e5e3
Tools • Dart 2.16.1 • DevTools 2.9.2
复制代码
  1. 选择要安装 Flutter 的操作系统,并按照官方操作流程进行 Flutter 环境的安装及验证:

  2. 在 VS Code 中开发,(个人建议使用轻量的 vscode 开发)

创建 Flutter 项目

Android Studio 中创建新项目:

  1. 选择 File>New Flutter Project
  2. 选择 Flutter application 作为 project 类型, 然后点击 Next
  3. 输入项目名称 (如 myapp), 然后点击 Next
  4. 点击 Finish
  5. 等待Android Studio安装SDK并创建项目

VS Code 中创建新项目:

  1. 在VS Code调用 View>Command Palette(快捷键 F1)
  2. 输入 ‘flutter’, 然后选择  ‘Flutter: New Project’ action
  3. 输入项目名称 (如 myapp), 然后按回车键
  4. 指定项目的存放位置,然后确定
  5. 等待项目创建继续,并显示main.dart文件

运行项目

  1. 准备虚拟机
    • mac 电脑可在终端执行 $ open -a Simulator 打开 iphone 虚拟机
    • 安装有 Android Studio 的同学可以在 Android Studio 中打开 安卓虚拟机
    • 无虚拟机,也可以使用 web 浏览器模式运行开发
  2. 在终端执行指令 flutter run 运行项目

路由导航配置

  1. 新建 lib/routers/routes.dart 文件:
import 'package:f_mall/views/detail/rank_index.dart';
import 'package:f_mall/views/detail/detail_index.dart';
import 'package:flutter/material.dart';

Route onGenerateRoute(RouteSettings settings) {
  switch (settings.name) {
    // 详情页面
    case '/detail':
      return MaterialPageRoute(
          builder: (_) => DetailIndex(), settings: settings);
    // 排行榜页面
    case '/rank':
      return MaterialPageRoute(
          builder: (_) => RankIndex(), settings: settings);
    // 404
    default:
      return MaterialPageRoute(
        builder: (_) => Scaffold(
          appBar: AppBar(
            title: const Text('404'),
          ),
          body: const Center(child: Text('Not Found.')),
        ),
      );
  }
}

复制代码
  1. MaterialApp 中 使用路由生成器属性 onGenerateRoute:
import 'package:f_mall/routers/routes.dart';
...

@override
Widget build(BuildContext context) {
    return MaterialApp(
      themeMode: ThemeMode.light,
      theme: appThemeData(context),
      darkTheme: appThemeData(context, isDark: true),
      title: 'Foodies Mall',
      onGenerateRoute: onGenerateRoute, // 路由生成器
      home: NavigationWithView(
        items: _navigationWithViewItems,
      ),
    );
}
复制代码

网络请求与数据模型

Http 请求

使用 dio 库,dio 封装查看: flutter_dio_util

import 'package:dio/dio.dart';

/// More examples see https://github.com/flutterchina/dio/tree/master/example
void main() async {
  var dio = Dio();
  final response = await dio.get('https://google.com');
  print(response.data);  
}

// response.data 这里返回的是json字符串,可使用 data['name'] 获取值
// 要想像 Object 一样使用 data.name 获取。需要转换为Dart模型(Json to Dart)
复制代码

Json to Dart

Person 模型,使用 quicktype 快速将 Json 转换 为 dart 模型

扫描二维码关注公众号,回复: 13798409 查看本文章
// 假设有如下person数据:
{
  "name": "jsdawn",
  "age": 18,
  "gender": ""
}
复制代码
// models/person.dart

// To parse this JSON data, do
//
//     final person = personFromJson(jsonString);

import 'dart:convert';

Person personFromJson(String str) => Person.fromJson(json.decode(str));

String personToJson(Person data) => json.encode(data.toJson());

class Person {
    Person({
        this.name,
        this.age,
        this.gender,
    });

    String name;
    int age;
    String gender;
  
    // json中字段不可少,否则需要判空:json["name"] ?? ""
    factory Person.fromJson(Map<String, dynamic> json) => Person(
        name: json["name"],
        age: json["age"],
        gender: json["gender"],
    );

    Map<String, dynamic> toJson() => {
        "name": name,
        "age": age,
        "gender": gender,
    };
}
复制代码

在 flutter 中使用:

// 这里 DefaultAssetBundle 的 loadString 加载json文件,模拟接口调用
String jsonString = await DefaultAssetBundle.of(context)
    .loadString('assets/json/data.json');
// 转为 dart model
final person = personFromJson(jsonString);
print(person.name);
复制代码

字体图标

方案一: 使用 iconfont_builder 工具

# --from 字体文件夹,--to 生成的Iconfont.dart文件存放路径,--family 自定义字体名
$ iconfont_builder --from ./fonts ,--to ./lib/Iconfont.dart --family MyIcon
复制代码

方案二:手动自定义字体图标(推荐)

  1. 引入图标字体:从 iconfont.cn 下载图标字体,将 demo_index.html 和 iconfont.ttf 复制到项目文件 my_project/fonts/my_icons
- my_project
    - lib
    - fonts
        - my_icons
            demo_index.html
            iconfont.ttf
复制代码
  1. 配置 flutter 文字资源,编辑 pubspec.yaml
fonts:
    - family: MyIcons
      fonts:
        - asset: fonts/my_icons/iconfont.ttf
复制代码
  1. 新建 MyIcons 类:
import 'package:flutter/widgets.dart';

/// 自定义 Icons 类
/// iconfont图标字体的 Unicode 可在第一步中的 demo_index.html 查看
class MyIcons {
  MyIcons._();

  // home iconfont图标字体的 Unicode 为 &#xe60f, IconData对应为 0xe60f 
  static const IconData home = IconData(0xe60f, fontFamily: 'MyIcons');
  
  // home iconfont图标字体的 Unicode 为 &#xe610, IconData对应为 0xe610 
  static const IconData user = IconData(0xe610, fontFamily: 'MyIcons');
}
复制代码

像官方一样使用

import 'package:f_mall/widgets/common/my_icons.dart';
...

Widget MyHomeIcon() {
  return const Padding(
    padding: EdgeInsets.all(5.0),
    // MyIcons.home
    child: Icon(MyIcons.home),
  );
}
复制代码

打包 apk

--no-sound-null-safety 跳过零空安全检查, --split-per-abi 减少包大小

flutter build apk --no-sound-null-safety --split-per-abi
复制代码

打包成功后,apk 存放在 build/app/outputs/apk/release/ 目录中

  • 若安卓打包 apk 后网络图片不显示

android/app/src/profile/AndroidManifest.xml 中允许网络权限(若无效则需要在 android/app/src/main/AndroidManifest.xml也配置一份):

<uses-permission android:name="android.permission.INTERNET"/>
复制代码
  • 安卓app签名(若需要发布 apk 到各应用市场)
  1. 终端执行下方指令获取秘钥文件,(文件默认在当前目录):
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
复制代码

将 key.jks 复制到 android/app/ 目录。

  1. android/key.properties 文件(没有则新建)
storePassword=<上一步骤中的密码>
keyPassword=<上一步骤中的密码>
keyAlias=key
storeFile=key.jks
复制代码
  1. android/app/build.gradle 中,android 代码块之前新增内容
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

// android 代码块
android {
    ...
}
复制代码

替换 buildTypes 代码块:

signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
        storePassword keystoreProperties['storePassword']
    }
}
buildTypes {
    release {
        signingConfig signingConfigs.release
    }
}
复制代码

结语

很荣幸你能看到这里。文章看上去简单,学习的路还是挺坎坷的。老样子,遇到问题的时候各种百度,综合多个解决方案选择最优解。期待你也能做出满意的作品...

转载声明: 请注明作者,注明原文链接,有疑问致邮 [email protected]

猜你喜欢

转载自juejin.im/post/7088233741751402532