Flutter Assets & Media

Flutter Assets & Media

概述

Flutter 应用程序由代码和资源(assets)两部分组成。资源是被打包到应用程序安装包中的文件,可在运行时访问。这些资源包括静态数据文件、配置文件、图标和各种媒体文件。

支持的资源类型

Flutter 支持多种资源类型:

  • 静态数据文件:JSON 配置文件、文本文件等
  • 图片格式:JPEG、WebP、GIF、动画 WebP/GIF、PNG、BMP、WBMP
  • 字体文件:TTF、OTF 等字体格式
  • 其他媒体文件:音频、视频等

资源配置

在 pubspec.yaml 中指定资源

在项目根目录的 pubspec.yaml 文件中,通过 flutter 部分的 assets 字段来指定应用程序所需的资源:

yaml 复制代码
flutter:
  assets:
    - assets/my_icon.png
    - assets/background.png

配置规则

  • 具体文件:可以指定具体的文件路径
  • 整个目录 :要包含某个目录下的所有资源,在目录名称后加上 /
yaml 复制代码
flutter:
  assets:
    - assets/images/
  • 子目录处理:目录配置仅包含当前目录下的直接文件,不包含子目录中的文件
  • 包含子目录:如需包含子目录文件,需为每个子目录单独创建条目

配置示例

yaml 复制代码
flutter:
  assets:
    - assets/
    - assets/images/
    - assets/fonts/
    - assets/data/config.json

资源变体(Asset Variants)

概念

Flutter 支持资源变体机制,允许为不同的设备分辨率或其他特性提供不同版本的资源文件。

目录结构示例

scss 复制代码
assets/
  images/
    icon.png          # 1.0x (基础分辨率)
    2.0x/
      icon.png        # 2.0x 分辨率
    3.0x/
      icon.png        # 3.0x 分辨率

配置方式

pubspec.yaml 中只需声明主资源:

yaml 复制代码
flutter:
  assets:
    - assets/images/icon.png

系统会自动识别并包含所有变体文件。

自动选择机制

在运行时,Flutter 会根据设备的像素密度自动选择最合适的资源变体:

  • 设备像素比为 2.0 时,加载 2.0x/icon.png
  • 设备像素比为 3.0 时,加载 3.0x/icon.png
  • 如果没有对应变体,则使用基础版本

资源加载

图片资源加载

Flutter 提供了多种图片加载方式,每种都适用于不同的场景:

1. 本地资源图片加载
使用 Image.asset()

最常用的本地图片加载方式:

dart 复制代码
Image.asset('assets/images/logo.png')

带参数的高级用法:

dart 复制代码
Image.asset(
  'assets/images/logo.png',
  width: 100,
  height: 100,
  fit: BoxFit.cover,
  alignment: Alignment.center,
  repeat: ImageRepeat.noRepeat,
  semanticLabel: '应用程序标志',
)
使用 AssetImage

用于需要 ImageProvider 的场景:

dart 复制代码
Container(
  decoration: BoxDecoration(
    image: DecorationImage(
      image: AssetImage('assets/images/background.png'),
      fit: BoxFit.cover,
    ),
  ),
)
2. 网络图片加载
使用 Image.network()

直接从网络 URL 加载图片:

dart 复制代码
Image.network('https://example.com/image.jpg')

带参数的用法:

dart 复制代码
Image.network(
  'https://example.com/image.jpg',
  width: 200,
  height: 200,
  fit: BoxFit.cover,
  loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
    if (loadingProgress == null) return child;
    return Center(
      child: CircularProgressIndicator(
        value: loadingProgress.expectedTotalBytes != null
            ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
            : null,
      ),
    );
  },
  errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
    return Icon(Icons.error);
  },
)
使用 NetworkImage

用于需要 ImageProvider 的场景:

dart 复制代码
Container(
  decoration: BoxDecoration(
    image: DecorationImage(
      image: NetworkImage('https://example.com/image.jpg'),
    ),
  ),
)
3. 文件系统图片加载
使用 Image.file()

从设备文件系统加载图片:

dart 复制代码
Image.file(File('/path/to/image.jpg'))
使用 FileImage

用于需要 ImageProvider 的场景:

dart 复制代码
Container(
  decoration: BoxDecoration(
    image: DecorationImage(
      image: FileImage(File('/path/to/image.jpg')),
    ),
  ),
)
4. 内存图片加载
使用 Image.memory()

从内存中的字节数据加载图片:

dart 复制代码
Image.memory(uint8List)
使用 MemoryImage

用于需要 ImageProvider 的场景:

dart 复制代码
Container(
  decoration: BoxDecoration(
    image: DecorationImage(
      image: MemoryImage(uint8List),
    ),
  ),
)
5. 高级加载方式
使用 FadeInImage 实现渐显效果
dart 复制代码
FadeInImage.assetNetwork(
  placeholder: 'assets/images/loading.gif',
  image: 'https://example.com/image.jpg',
  fadeInDuration: Duration(milliseconds: 300),
)

从网络加载,本地占位:

dart 复制代码
FadeInImage.memoryNetwork(
  placeholder: kTransparentImage, // 需要 import 'package:transparent_image/transparent_image.dart';
  image: 'https://example.com/image.jpg',
)
使用 CachedNetworkImage(第三方包)

首先在 pubspec.yaml 中添加依赖:

yaml 复制代码
dependencies:
  cached_network_image: ^3.2.3

然后使用:

dart 复制代码
CachedNetworkImage(
  imageUrl: 'https://example.com/image.jpg',
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
  fadeInDuration: Duration(milliseconds: 300),
  memCacheWidth: 200,
  memCacheHeight: 200,
)
图片加载方式选择指南
图片来源 推荐方式 使用场景
应用内静态资源 Image.asset() 应用图标、背景图、UI 装饰图
网络资源 Image.network()CachedNetworkImage 用户头像、动态内容图片
设备文件系统 Image.file() 用户选择的照片、相机拍摄的图片
内存字节数据 Image.memory() 经过处理的图片数据、生成的图片
性能优化建议
  1. 使用合适的图片格式:WebP 格式通常比 PNG/JPEG 更小
  2. 提供多分辨率资源:使用 1.0x、2.0x、3.0x 资源变体
  3. 网络图片缓存 :使用 CachedNetworkImage 避免重复下载
  4. 控制图片尺寸 :使用 widthheight 参数避免加载过大图片
  5. 懒加载:对于列表中的图片,考虑使用懒加载机制

文本资源加载

使用 rootBundle
dart 复制代码
import 'package:flutter/services.dart' show rootBundle;

Future<String> loadAsset() async {
  return await rootBundle.loadString('assets/data/config.json');
}
使用 DefaultAssetBundle
dart 复制代码
Future<String> loadAssetWithContext(BuildContext context) async {
  return await DefaultAssetBundle.of(context).loadString('assets/data/config.json');
}

二进制资源加载

dart 复制代码
import 'package:flutter/services.dart' show rootBundle;

Future<ByteData> loadBinaryAsset() async {
  return await rootBundle.load('assets/audio/sound.mp3');
}

字体管理

字体配置

pubspec.yaml 中配置自定义字体:

yaml 复制代码
flutter:
  fonts:
    - family: Raleway
      fonts:
        - asset: fonts/Raleway-Regular.ttf
        - asset: fonts/Raleway-Italic.ttf
          style: italic
        - asset: fonts/Raleway-Bold.ttf
          weight: 700
    - family: RobotoMono
      fonts:
        - asset: fonts/RobotoMono-Regular.ttf
        - asset: fonts/RobotoMono-Bold.ttf
          weight: 700

字体使用

dart 复制代码
Text(
  'Hello, Flutter!',
  style: TextStyle(
    fontFamily: 'Raleway',
    fontSize: 18,
    fontWeight: FontWeight.bold,
    fontStyle: FontStyle.italic,
  ),
)

字体权重配置

yaml 复制代码
fonts:
  - family: MyFont
    fonts:
      - asset: fonts/MyFont-Thin.ttf
        weight: 100
      - asset: fonts/MyFont-Light.ttf
        weight: 300
      - asset: fonts/MyFont-Regular.ttf
        weight: 400
      - asset: fonts/MyFont-Medium.ttf
        weight: 500
      - asset: fonts/MyFont-Bold.ttf
        weight: 700
      - asset: fonts/MyFont-Black.ttf
        weight: 900

Android 原生应用图标资源文件、变量、xml 文件引用

AndroidManifest.xml 引用应用图标资源

Android/app/src/main/res/mipmap/ic_launcher/不同 dpi 的 ic_launcher.png

Android/app/src/main/res/mipmap/ic_launcher_round/不同 dpi 的 ic_launcher_round.png

xml 复制代码
 <application
    android:icon="@mipmap/ic_launcher"
    android:roundIcon="@mipmap/ic_launcher_round">
        ....
</application>

_ 注意文件夹和文件名称相同只是分别率不同 _

AndroidManifest.xml 引用变量、xml 文件

strings.xml(Android)定义字符串变量

Android/app/src/main/res/values/strings.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">kotlin</string>
    <string name="navigation_drawer_open">Open navigation drawer</string>
    <string name="navigation_drawer_close">Close navigation drawer</string>
    <string name="nav_header_title">Android Studio</string>
    <string name="nav_header_subtitle">android.studio@android.com</string>
    <string name="nav_header_desc">Navigation header</string>
    <string name="action_settings">Settings</string>
    <string name="menu_home">Home</string>
    <string name="menu_gallery">Gallery</string>
    <string name="menu_slideshow">Slideshow</string>
</resources>
colors.xml(Android)定义颜色变量

Android/app/src/main/res/values/colors.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>
themes.xml(Android)定义颜色变量

Android/app/src/main/res/values/themes/themes.xml

xml 复制代码
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Kotlin" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
    <style name="Theme.Kotlin.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>
    <style name="Theme.Kotlin.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
    <style name="Theme.Kotlin.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
AndroidManifest.xml 引用 colors.xml、strings.xml、themes.xml 变量

Android 会去 values 目录下递归所有目录下的文件夹下的 xml 文件。

xml 复制代码
<application
        android:label="@string/app_name"
        android:theme="@style/Theme.Kotlin">
        <activity
            android:label="@string/app_name"
           >
          ....
        </activity>
    </application>
styles.xml(Android)定义样式变量

Android/app/src/main/res/values/styles.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
      <!-- 这里注意@drawable/launch_background是在drawable目录下的一个launch_background.xml文件:具体目录:
Android/app/src/main/res/drawable/launch_background.xml -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
</resources>

Android 原生和 Flutter 对比总结

AndroidManifest.xml 位置

Android Flutter
Android/app/manifests/AndroidManifest.xml Android/app/src/main/AndroidManifest.xml

变量 xml 文件对比

  1. Android 和 Flutter 都会去 values 目录下递归所有目录下的文件夹下的 xml 文件。
Android Flutter
Android/app/src/main/res/values/*/ Android/app/src/main/res/values/*/
  1. Flutter 会区分 night 文件目录,而 Android 不会。
Android Flutter
Android/app/src/main/res/values/themes/themes.xml、Android/app/src/main/res/values/themes/themes.xml(night) Android/app/src/main/res/values/style.xml、Android/app/src/main/res/values-night/style.xml
  1. Android 和 Flutter 的变量、文件引用方式相同
Android Flutter
@string/app_name、@color/primary、@style/Theme.Kotlin 、@drawable/launch_background @string/app_name、@color/primary、@style/Theme.Kotlin 、@drawable/launch_background
  1. Android 和 Flutter 的变量文件定义变量规则相同
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path name="root" path="" />
        <files-path name="files" path="" />
        <cache-path name="cache" path="" />
        <external-path name="external" path="" />
        <external-files-path name="external_files" path="" />
        <external-cache-path name="external_cache" path="" />
    </paths>
</resources>

Flutter 平台特定资源

Android 平台

应用图标配置
  • 路径:android/app/src/main/res/
  • 文件:各 mipmap-* 文件夹中的 ic_launcher.png
  • 要求:按照 Android 开发者指南提供不同分辨率的图标
  • 注意:如果您重命名了.png 文件,您还必须在 AndroidManifest.xml 的标签的 android:icon android:icon="@mipmap/ic_launcher" 属性中更新相应的名称。
所需尺寸规格:
  • mipmap-ldpi/ (0.75x) - 36x36px (基准 48px)
  • mipmap-mdpi/ (1.0x) - 48x48px (基准尺寸)
  • mipmap-hdpi/ (1.5x) - 72x72px
  • mipmap-xhdpi/ (2.0x) - 96x96px
  • mipmap-xxhdpi/ (3.0x) - 144x144px
  • mipmap-xxxhdpi/ (4.0x) - 192x192px
使用在线工具生成
  1. Android Asset Studio (官方网站)
  • 网址:romannurik.github.io/AndroidAsse...
  • 功能:自动生成各种分辨率的图标
  • 上传一张高分辨率图片(建议 1024x1024px)
  • 自动生成所有 Android 密度的图片
  1. MakeAppIcon
  • 网址:makeappicon.com/
  • 同时支持 iOS 和 Android
  • 功能:自动生成各种分辨率的图标
  • 建议:上传一张高分辨率图片(建议 1024x1024px)
  • 自动生成所有 iOS 和 Android 密度的图片
应用 Label 配置
  • 路径:android/app/src/main/res/AndroidManifest.xml
  • 应用: Label 会应用 AndroidManifest.xml 的标签的 android:label 属性中相应的名称。
  • 注意:如果您重新名称,您必须在 AndroidManifest.xml 的标签的 android:label 属性中更新相应的名称。
完整示例代码
xml 复制代码
 <application
        android:networkSecurityConfig="@xml/network_security_config"
        android:name=".MyApp"
        android:label="xxxxx xxxx xxxx"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        >
        ....
</application>

iOS 平台

应用图标配置
  • 路径:ios/Runner/Assets.xcassets/AppIcon.appiconset/
  • 要求:提供 iOS 要求的各种尺寸图标文件
所需尺寸规格:

略(直接使用自动生成的图片。规格太多了不讲了)

使用在线工具生成
  1. MakeAppIcon
  • 网址:makeappicon.com/
  • 同时支持 iOS 和 Android
  • 功能:自动生成各种分辨率的图标
  • 建议:上传一张高分辨率图片(建议 1024x1024px)
  • 自动生成所有 iOS 和 Android 密度的图片
应用 Label 配置
  • 路径:ios/Runner/Info.plist
  • 应用: Label 会应用 Info.plist 的CFBundleName标签下的xxxxx xxxx xxxx 属性中相应的名称。
  • 注意:如果您重新名称,您必须在 Info.plist 的CFBundleName标签下的xxxxx xxxx xxxx 属性更新相应的名称。
xml 复制代码
<key>CFBundleName</key>
<string>xxxxx xxxx xxxx</string>

Flutter 更新启动图(也叫闪屏页(也称为启动页))

简单梳理:只展示图片,更高级的动画展示需要研读 Android 和 iOS 的启动图配置。

概念

在 Flutter 框架加载时,Flutter 会使用原生平台机制绘制启动页。此启动页将持续到 Flutter 渲染应用程序的第一帧。

这意味着如果你不在应用程序的 main() 方法中调用 runApp() 函数(或者更具体地说,如果你不调用 FlutterView.render() 去响应 PlatformDispatcher.onDrawFrame 的话,启动页将永远持续显示。

完成一个启动页的配置需要满足俩个条件:1.启动页图片资源,背景(定义在 drawable 目录下的不同 xml 中:例如 launch_background.xml、normal_background.xml....);2.主题一般定义在 values 目录下的 themes.xml、style.xml...文件中文件命名看个人习惯。(主题的设置主要源于移动端暗模式、亮模式)

只要为什么是 drawable 目录吗?android 默认是 drawable 目录。且他们在安卓 api 中就是这样叫的。

Android

步骤

这只是一个简单示例,用于将图片添加到白色启动页的中间。当然可绘制对象资源来实现预期效果。更高级的动画展示需要系统学习 Android 的 XML 编写。

第一步:定义启动页图片资源、背景
xml 复制代码
<!-- launch_background.xml 文件 -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/bg_launch_color" />
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/launch_image" />
    </item>
</layer-list>
<!-- normal_background.xml 文件 -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/bg_normal_color" />
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/normal_image" />
    </item>
</layer-list>
第二步:定义主题 引用启动页图片资源、背景

在 styles.xml 或者 themes.xml 中应用不同的主题:launch_background.xml、normal_background.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
    <item name="android:windowBackground">@drawable/launch_background</item>
  </style>
  <style name="HomeTheme" parent="@android:style/Theme.Black.NoTitleBar">
    <item name="android:windowBackground">@drawable/home_background</item>
  </style>
</resources>
第三步:在 AndroidManifest.xml 中引用主题
xml 复制代码
<application
   // ...
   >
<activity
    android:name=".MyActivity"
    android:theme="@style/LaunchTheme"
    // ...
    >
   // ...
</activity>
    //...
</application>
<!-- 或者 -->
<application
   // ...
   >
<activity
    android:name=".MyActivity"
    android:theme="@style/HomeTheme"
    // ...
    >
   // ...
</activity>
    //...
</application>
第四步: 普通主题在 AndroidManifest.xml 中引用

当启动页消失后,它会应用在 FlutterActivity 上。普通主题的背景仅仅展示非常短暂的时间,例如,当启动页消失后、设备方向改变或者 Activity 恢复期间。因此建议普通主题的背景颜色使用与 Flutter UI 主要背景颜色相似的纯色。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
    <item name="android:windowBackground">@drawable/launch_background</item>
  </style>
  <style name="HomeTheme" parent="@android:style/Theme.Black.NoTitleBar">
    <item name="android:windowBackground">@drawable/home_background</item>
  </style>
  <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
    <item name="android:windowBackground">@drawable/normal_background</item>
  </style>
</resources>
第五步: 普通主题在 AndroidManifest.xml 中引用
xml 复制代码
<application
   // ...
   >
<activity
    android:name=".MyActivity"
    android:theme="@style/LaunchTheme"
    // ...
    >
   <meta-data
        android:name="io.flutter.embedding.android.NormalTheme"
        android:resource="@style/NormalTheme"
        />
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>
    //...
</application>
<!-- 或者 -->
<application
   // ...
   >
<activity
    android:name=".MyActivity"
    android:theme="@style/HomeTheme"
    // ...
    >
   <meta-data
        android:name="io.flutter.embedding.android.NormalTheme"
        android:resource="@style/NormalTheme"
        />
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>
    //...
</application>

如此一来,Android 应用程序就会在在初始化时展示对应的启动页面和普通主题。

iOS

将图片添加到启动屏幕「splash screen」的中心,请导航至 .../ios/Runner 路径。在 Assets.xcassets/LaunchImage.imageset ,拖入图片,并命名为 LaunchImage.png, LaunchImage@2x.pngLaunchImage@3x.png。如果你使用不同的文件名,那你还必须更新同一目录中的 Contents.json 文件中对应的名称。

相关推荐
林间风雨12 小时前
flutter项目 -- 换logo、名称 、签名、打包
flutter
SoaringHeart1 天前
Flutter组件封装:页面点击事件拦截
前端·flutter
tangweiguo030519871 天前
Flutter与原生混合开发:实现完美的暗夜模式同步方案
android·flutter
程序员老刘1 天前
CTO紧急叫停AI编程!不是技术倒退,而是...
flutter·ai编程
leazer1 天前
Flutter TabBar 字体缩放动画抖动问题及优化方案
flutter
yuanpan1 天前
认识跨平台UI框架Flutter和MAUI区别,如何选。
flutter·ui·maui
无知的前端1 天前
一文精通-Flutter 状态管理
flutter
阿笑带你学前端1 天前
Drift数据库开发实战:类型安全的SQLite解决方案
前端·flutter
农夫三拳_有点甜1 天前
Flutter MaterialApp 组件属性第一章
flutter