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 文件中对应的名称。

相关推荐
傅里叶4 小时前
Flutter项目使用 buf.build
flutter
恋猫de小郭6 小时前
iOS 26 开始强制 UIScene ,你的 Flutter 插件准备好迁移支持了吗?
android·前端·flutter
yuanlaile6 小时前
Flutter开发HarmonyOS鸿蒙App商业项目实战已出炉
flutter·华为·harmonyos
CodeCaptain7 小时前
可直接落地的「Flutter 桥接鸿蒙 WebSocket」端到端实施方案
websocket·flutter·harmonyos
stringwu7 小时前
Flutter 中的 MVVM 架构实现指南
前端·flutter
消失的旧时光-194320 小时前
Flutter 异步体系终章:FutureBuilder 与 StreamBuilder 架构优化指南
flutter·架构
消失的旧时光-19431 天前
Flutter 异步 + 状态管理融合实践:Riverpod 与 Bloc 双方案解析
flutter
程序员老刘1 天前
Flutter版本选择指南:避坑3.27,3.35基本稳定 | 2025年10月
flutter·客户端
—Qeyser1 天前
Flutter网络请求Dio封装实战
网络·flutter·php·xcode·android-studio