首页 > 其他分享 >Flutter 陈航 16-主题定制 依赖管理 静态资源 三方库

Flutter 陈航 16-主题定制 依赖管理 静态资源 三方库

时间:2023-01-06 02:44:06浏览次数:56  
标签:依赖 assets 16 Pub 陈航 版本 Flutter 资源

本文地址


目录

目录

16 | 从夜间模式说起,如何定制主题?

在 iOS 中,我们通常会将主题的配置信息预先写到 plist 文件中,通过一个单例来控制 App 应该使用哪种配置;而 Android 的配置信息则写入各个 style 属性值的 xml 中,通过 activity 的 setTheme 进行切换;前端的处理方式也类似,简单更换 css 就可以实现多套主题/配色之间的切换。

Flutter 也提供了类似的能力,由 ThemeData 来统一管理主题的配置信息。

通过 ThemeData,可以实现 App 全局范围,或是 Widget 局部范围的样式切换。

全局统一的视觉风格定制

在 Flutter 中,应用程序类 MaterialApp 的初始化方法,为我们提供了设置主题的能力。我们可以通过参数 theme,选择改变 App 的主题色、字体等,设置界面在 MaterialApp 下的展示样式。

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: const HomePage(title: '白乾涛'),
        theme: ThemeData(
          brightness: Brightness.dark, // 明暗模式
          primaryColor: Colors.green, // 应用主色调
          iconTheme: const IconThemeData(color: Colors.red), // icon 主题色
        ),
      );
}

局部独立的视觉风格定制

在 Flutter 中可以使用 Theme 来对 App 的主题进行局部覆盖。Theme 是一个单子 Widget 容器,可以通过设置其 data 属性,对其子 Widget 进行样式定制:

  • 如果我们不想继承任何 App 全局的颜色或字体样式,可以直接新建一个 ThemeData 实例
  • 如果我们想继承 App 的主题,并只更新部分样式,可以使用 copyWith 方法
class _HomePageState extends State<HomePage> {
  double size = 30;

  @override
  Widget build(BuildContext context) {
    ThemeData theme1 = ThemeData(iconTheme: const IconThemeData(color: Colors.blue));
    ThemeData theme2 = Theme.of(context).copyWith(iconTheme: const IconThemeData(color: Colors.green));
    Icon icon1 = Icon(Icons.thumb_up, size: size);
    Icon icon2 = Icon(Icons.favorite, size: size);

    return Scaffold(
        appBar: AppBar(title: Text(widget.title)),
        body: Column(
          children: [
            Row(children: [Theme(data: theme1, child: icon1), Theme(data: theme1, child: icon2)]),
            Row(children: [Theme(data: theme2, child: icon1), Theme(data: theme2, child: icon2)]),
            Row(children: [icon1, icon2]),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => setState(() => size += 10),
          child: Text(size.toInt().toString()),
        ));
  }
}

获取样式特定的值

我们可以通过 Theme.of(context).xxx 取出样式某一属性的值。

Theme.of(context) 将向上查找 Widget 树,并返回 Widget 树中最近的主题 Theme。如果 Widget 的父 Widget 们有一个单独的主题定义,则使用该主题。如果都没有,那就使用 App 全局主题。

在下面的例子中,我们创建了一个包装了一个 Text 组件的 Container 容器。在 Text 组件的样式定义中,我们复用了全局的 title 样式,而在 Container 的背景色定义中,则复用了 App 的主题色:

Container(
   color: Theme.of(context).primaryColor, // 容器背景色复用应用主题色
   child: const Text('primaryColor'),
)

根据平台选择主题

我们可以根据 defaultTargetPlatform 来判断当前应用所运行的平台,从而根据系统类型来设置对应的主题。

TargetPlatform platform = defaultTargetPlatform; // package:flutter/foundation.dart
theme: platform == TargetPlatform.iOS ? kIOSTheme : kAndroidTheme, // 根据平台选择主题

17 | 依赖管理:图片、配置和字体

原文

资源管理

在 Flutter 中,资源(assets)可以是任意类型的文件。

Flutter 并没有像 Android 那样预先定义资源的目录结构,我们可以把资源存放在项目中的任意目录下,只需要在根目录下的 pubspec.yaml 文件中,对这些资源的所在位置进行显式声明就可以了。

声明时,我们既可以对每一个文件进行挨个声明,也可以采用子目录批量声明的方式。

assets
├── background.jpg
├── icons
│   ├── food1.jpg
│   └── food2.jpg
├── loading.gif
└── result.json

对于上述资源文件存放的目录结构,以下代码分别演示了挨个指定和子目录批量指定这两种方式:通过单个文件声明的,我们需要完整展开资源的相对路径;而对于目录批量指定的方式,只需要在目录名后加路径分隔符就可以了:

flutter:
  assets:
    - assets/background.jpg # 挨个指定资源路径
    - assets/loading.gif    # 挨个指定资源路径
    - assets/result.json    # 挨个指定资源路径
    - assets/icons/         # 子目录批量指定
    - assets/               # 根目录也可以批量指定

注意:目录批量指定并不递归,只有在该目录下的文件才可以被包括,如果下面还有子目录的话,需要单独声明子目录下的文件。

完成资源的声明后,我们就可以在代码中访问它们了。

  • 图片资源:通过 Image.asset 访问
  • 字符串文件:通过 rootBundle.loadString 访问
  • 二进制文件:通过 rootBundle.load 访问
class _HomePageState extends State<HomePage> {
  String _text = "1";

  void _onPressed() => rootBundle.loadString('assets/result.json').then((msg) {
        _text = msg;
        setState(() => flog(_text));
      });

  @override
  Widget build(BuildContext context) => Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Column(children: [Text(_text), Image.asset("assets/images/monkey.jpg")]),
      floatingActionButton: FloatingActionButton(
        onPressed: _onPressed,
        child: Text(_text),
      ));
}

基于像素密度的图片

How to Find Device Metrics for Any Screen

与 Android、iOS 开发类似,Flutter 也遵循了基于像素密度的管理方式,如 1.0x、2.0x、3.0x 或其他任意倍数,Flutter 可以根据当前设备分辨率加载最接近设备像素比例的图片资源。

为了让 Flutter 更好地识别,我们的资源目录应该将 1.0x、2.0x 与 3.0x 的图片资源分开管理。

assets
├── background.jpg     // 1.0x 图
├── 2.0x
│   └── background.jpg // 2.0x 图
└── 3.0x
    └── background.jpg // 3.0x 图

而在 pubspec.yaml 文件声明这个图片资源时,仅声明 1.0x 图资源即可:

flutter:
  assets:
    - assets/background.jpg # 1.0x 图资源

注意:1.0x 分辨率的图片是资源标识符,即使没有包含 1.0x 资源,仍需在 pubspec.yaml 中将它显示声明出来。

Flutter 会根据实际屏幕像素比例加载相应分辨率的图片,如果主资源缺少某个分辨率资源,Flutter 会在剩余的分辨率资源中选择最接近的分辨率资源去加载。

字体

在 Flutter 中,使用自定义字体同样需要在 pubspec.yaml 文件中提前声明。

fonts:
  - family: RobotoCondensed                             # 字体名
    fonts:
      - asset: assets/fonts/RobotoCondensed-Regular.ttf # 普通字体
      - asset: assets/fonts/RobotoCondensed-Italic.ttf
        style: italic                                   # 斜体
      - asset: assets/fonts/RobotoCondensed-Bold.ttf
        weight: 700                                     # 粗体

这些声明都对应着 TextStyle 中的样式属性:

  • 字体名 family 对应 fontFamily 属性
  • 字体样式 style 对应 style 属性
    • 斜体 italic
    • 正常体 normal
  • 字体粗细 weight 对应 fontWeight 属性

在使用时,我们只需要在 TextStyle 中指定对应的字体即可:

Text("普通字体", style: TextStyle(fontFamily: 'RobotoCondensed'));
Text("粗体", style: TextStyle(fontFamily: 'RobotoCondensed', fontWeight: FontWeight.w700));
Text("斜体", style: TextStyle(fontFamily: 'RobotoCondensed', fontStyle: FontStyle.italic));

原生平台的资源

Flutter 需要原生环境才能运行,但是有些资源我们需要在 Flutter 框架运行之前提前使用,这时就需要在对应的原生工程中完成相应的配置。

  • 更换 App 启动图标

对于 Android 平台,启动图标位于根目录 android/app/src/main/res/mipmap 下。我们只需要遵守对应的像素密度标准,保留原始图标名称,将图标更换为目标资源即可:

  • 更换启动图

对于 Android 平台,启动图位于根目录 android/app/src/main/res/drawable 下,是一个名为 launch_background 的 XML 文件。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 白色背景 -->
    <item android:drawable="@android:color/white" />
    <item>
         <!-- 内嵌一张居中展示的图片 -->
        <bitmap
            android:gravity="center"
            android:src="@mipmap/bitmap_launcher" />
    </item>
</layer-list>

18 | 依赖管理:第三方组件库如何管理?

pubspec.yaml 与 iOS 中的 Podfile、Android 中的 build.gradle、前端的 package.json 在功能上是类似的。

除了管理资源,其更重要的作用是管理 Flutter 工程代码的依赖,比如第三方库、Dart 运行环境、Flutter SDK 版本。

Pub

与 Android 中的 JCenter/Maven、iOS 中的 CocoaPods、前端中的 npm 库类似,Dart 提供了官方的包仓库 Pub。通过 Pub,我们可以很方便地查找到有用的第三方包。

在 Dart 中,库和应用都属于包

pubspec.yaml

pubspec.yaml 是包的配置文件,包含了包的元数据(包的名称和版本)、运行环境(Dart SDK 与 Fluter SDK 版本)、外部依赖、内部配置(资源管理)。

name: flutter_app_demo   # 应用名称
description: 应用描述.    # 应用描述
version: 1.0.0           # 应用版本
environment:             # Dart 运行环境区间
  sdk: ">=2.1.0 <3.0.0"  # 支持 2.1 至 3.0 之间的 Dart 版本
dependencies:            # 依赖库
  flutter:
    sdk: flutter
  cupertino_icons: ">0.1.1" # 版本约束信息,引用任意大于 0.1.1 的版本

版本约束方式

版本约束信息支持指定版本版本号区间任意版本这三种版本约束方式。

注意:

  • 由于元数据与名称使用空格分隔,因此版本号中不能出现空格
  • 由于大于符号>是 YAML 语法中的折叠换行符号,因此在指定版本范围的时候,必须使用引号

对于包,我们通常是指定版本区间,而很少直接指定特定版本,因为包升级变化很频繁,如果有其他的包直接或间接依赖这个包的其他版本时,就会经常发生冲突。

environment

建议将 Dart 与 Flutter 的 SDK 环境写死,统一团队的开发环境

environment:
  sdk: 2.3.0
  flutter: 1.2.1

数据源

只有在 Pub 上进行公开发布过的包,才可以以版本的方式引用。否则,我们需要设置数据源,使用本地路径或 Git 地址的方式进行包声明。

dependencies:
  package1:
    path: ../package1/                         # 路径依赖
  date_format:
    git:
      url: https://github.com/xxx/package2.git # git 依赖

pubspec.lock

在开发应用时,我们可以不写明具体的版本号,而是以区间的方式声明包的依赖;但对于一个程序而言,其运行时具体引用哪个版本的依赖包必须要确定下来。因此,包管理工具 Pub 的另一个职责是,找出一组同时满足每个包版本约束的包版本。包版本一旦确定,接下来就是下载对应版本的包了。

对于 dependencies 中的不同数据源,Dart 会使用不同的方式进行管理,最终会将远端依赖的包全部下载到本地。

  • 对于 Git 声明依赖的方式,Pub 会 clone Git 仓库
  • 对于版本号的方式,Pub 则会从 pub.dartlang.org 下载包
  • 如果包还有其他的依赖包,Pub 也会一并下载

在完成了所有依赖包的下载后,Pub 会在应用的根目录下创建 .packages 文件,将依赖的包名与系统缓存中的包文件路径进行映射,方便后续维护。

最后,Pub 会自动创建 pubspec.lock 文件,其作用类似 iOS 的 Podfile.lock 或前端的 package-lock.json 文件,用于记录当前状态下实际安装的各个直接依赖、间接依赖的包的具体来源和版本号

比较活跃的第三方包的升级通常比较频繁,因此我们需要把 pubspec.lock 文件也一并提交到代码版本管理中,这样团队中的所有人在使用这个应用时安装的所有依赖都是完全一样的。

现代编程语言大都自带第依赖管理机制,其核心功能是为工程中所有直接或间接依赖的代码库找到合适的版本,但这并不容易。

就比如前端的依赖管理器 npm 的早期版本,就曾因为不太合理的算法设计,导致计算依赖耗时过长,依赖文件夹也高速膨胀,一度被开发者们戏称为“黑洞”。

而 Dart 使用的 Pub 依赖管理机制所采用的 PubGrub 算法则解决了这些问题,因此被称为下一代版本依赖解决算法,在 2018 年底被苹果公司吸纳,成为 Swift 所采用的依赖管理器算法。

当然,如果你的工程里的依赖比较多,并且依赖关系比较复杂,即使再优秀的依赖解决算法也需要花费较长的时间才能计算出合适的依赖库版本。如果我们想减少依赖管理器为你寻找代码库依赖版本所耗费的时间,一个简单的做法就是从源头抓起,在 pubspec.yaml 文件中固定那些依赖关系复杂的第三方库们,及它们递归依赖的第三方库的版本号。

依赖包的资源

除了提供功能和代码维度的依赖之外,包还可以提供资源的依赖。

例如,我们的应用依赖了一个名为 package_qt 的包,其包含一个 icon_qt.png 的图片,那么,在我们应用中,就可以通过 Image 和 AssetImage 提供的 package 参数去加载依赖包中的图片。

Image.asset('assets/icon_qt.png', package: 'package_qt');
AssetImage('assets/icon_qt.png', package: 'package_qt');

案例

date_format 提供了若干常用的日期格式化方法,可以很方便地实现格式化日期的功能。

首先,我们在 Pub 上找到 date_format 这个包:

date_format: ^2.0.7
A simple API to format dates.

Use formatDate function to format a DateTime object.
print(formatDate(DateTime(1989, 02, 21), [yyyy, '-', mm, '-', dd]));
1989-02-21

把 date_format 添加到 pubspec.yaml 中:

dependencies:
  flutter:
    sdk: flutter
  date_format: ^2.0.7

随后,IDE 监测到了配置文件的改动,提醒我们进行安装包依赖更新。于是,我们点击 Get dependenciesPub get,下载 date_format。

下载完成后就可以在工程中使用了:

flog(formatDate(DateTime.now(), [mm, '月', dd, '日', hh, ':', n])); // 01月06日02:23
flog(formatDate(DateTime.now(), [m, '月第', w, '周'])); // 1月第1周

2023-1-6

标签:依赖,assets,16,Pub,陈航,版本,Flutter,资源
From: https://www.cnblogs.com/baiqiantao/p/17029308.html

相关文章

  • 【LeeCode】416. 分割等和子集 -- todo
    【题目描述】给你一个 只包含正整数 的 非空 数组 ​​nums​​ 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。​​​​https://leetcode.c......
  • [leetcode]第 16 天 排序(简单)、第 17 天 排序(中等)
    面试题45.把数组排成最小的数思路使用到了快速排序classSolution{publicStringminNumber(int[]nums){//初始化字符串数组String[]strs......
  • 适合编程初学者的开源项目:小游戏2048(Flutter版)
    目标为编程初学者打造入门学习项目,使用各种主流编程语言来实现。2048游戏规则一共16个单元格,初始时由2或者4构成。1、手指向一个方向滑动,所有格子会向那个方向运动。2......
  • NC16466 [NOIP2015]信息传递
    题目链接题目题目描述有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti的同......
  • [LeetCode]016-最接近的三数之和
    >>>传送门题目给你一个长度为n的整数数组 nums 和一个目标值 target。请你从nums中选出三个整数,使它们的和与 target 最接近。返回这三个数的和。假定每组......
  • Codeforces Contest 1616
    A.IntegerDiversity直接用个map贪心,如果有相同的就反向即可。B.MirrorintheString这道题洛谷的翻译锅了,所以建议去看原题。考虑这样一个字符串baacc,那么答案显......
  • NC16416 [NOIP2017]逛公园
    题目链接题目题目描述策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有......
  • 24*8点 段码LCD液晶显示驱动控制电路(IC/芯片)-VK0192M 具省电模式,可兼容替代市面1622
    产品品牌:永嘉微电/VINKA产品型号:VK0192M封装形式:LQFP44概述:VK0192MLQFP44是一个点阵式存储映射的LCD驱动器,可支持最大192点(24SEGx8COM)的LCD屏。单片机可通过3/4线串行......
  • 亚马逊攀岩绳、动力绳EN 892:2012+A1:2016测试标准流程
    攀岩绳是与攀岩安全带和锚点相连的一种装备,用于保护攀岩者,使其不会从高处跌落。攀岩绳由承重内芯和围绕内芯编织的护套组成。根据亚马逊要求所有攀岩装备上架亚马逊都是需要......
  • flutter webview_windows 简单使用
    class_MyHomePageStateextendsState<MyHomePage>{finalWebviewController_webViewController=WebviewController();@overridevoidinitState(){sup......