Flutter 国际化实现指南

Flutter 实现国际化(i18n)主要有两部分:Flutter SDK 内置组件的国际化和’开发者自定义 UI 的国际化‘。

1. 原理概述

  1. 默认语言
    Flutter SDK 为了减小包体积,默认只提供美国英语(en-US)的本地化资源,主要是 Material 组件和基础 Widgets 的文本。
  2. 本地化的核心
    • Localizations:Flutter 用于管理不同语言资源的核心类
    • LocalizationsDelegate:工厂类,用于生成对应语言的资源
    • supportedLocales:告诉 Flutter 应用支持哪些语言和地区
  3. 适用范围
    • Material 组件(如按钮、日期选择器、对话框等)
    • Widgets(Tooltip、日期等基础文本)
    • Cupertino 组件(iOS 风格组件文本)
    • 自定义 UI 文本(开发者实现)

      这些组件本身有默认文本(如按钮文字、取消/确定、返回按钮提示等)。

2. 使用 Flutter 内置国际化(Material、Widgets、Cupertino)

添加依赖

pubspec.yaml 文件中添加:

1
2
3
4
5
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter

然后执行:

1
flutter pub get

2. 配置 MaterialApp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void main() {
runApp(const MyApp());
}

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

@override
Widget build(BuildContext context) {
return MaterialApp(
title:'国际化示例',
// 1. 指定本地化代理
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate, // Material 组件
GlobalWidgetsLocalizations.delegate, // 基础 Widgets
GlobalCupertinoLocalizations.delegate, // iOS 组件
],

// 2. 指定支持语言
supportedLocales: const [
Locale(';en',';'), // 英文
Locale(';zh',';'), // 中文
],

home: const MyHomePage(),
);
}
}

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

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('国际化示例')),
body: Center(
child: Text(MaterialLocalizations.of(context).backButtonTooltip),
),
);
}
}

说明:

  • localizationsDelegates:指定哪些 Widget 需要国际化
  • supportedLocales:指定支持的语言
  • MaterialLocalizations.of(context) 可以获取 Material 组件的本地化文本

3️⃣ 自定义 UI 文本国际化

1. 使用 intl 包(推荐)

pubspec.yaml 中添加:

1
2
dependencies:
intl: ^0.18.1

2. 创建 ARB 文件

lib/l10n/intl_en.arb

1
2
3
4
5
6
{
"hello": "Hello",
"@hello": {
"description": "Greeting message"
}
}

lib/l10n/intl_zh.arb

1
2
3
4
5
6
7
{
"hello": "你好",
"@hello": {
"description": "问候语"
}
}

3. 生成 Dart 文件

1
flutter pub run intl_utils:generate

会生成 app_localizations.dart,用于获取翻译文本

4. 在 MaterialApp 中注册

1
2
3
4
5
6
7
8
9
10
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate, // 自定义 UI 文本
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
)

5. 使用自定义文本

1
Text(AppLocalizations.of(context)!.hello)

4️⃣ iOS 配置

在 iOS 需要在 Info.plist 添加 CFBundleLocalizations,列出支持语言:

1
2
3
4
5
6
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>zh</string>
</array>

Flutter 会根据系统语言自动选择对应 Locale


5️⃣ 小结

  1. Flutter 默认只提供英语
  2. Material、Widgets、Cupertino 组件通过 flutter_localizations 提供多语言
  3. 自定义 UI 文本需使用 Localizationsintl
  4. localizationsDelegates 控制组件本地化
  5. supportedLocales 控制应用支持语言
  6. iOS 需在 Info.plist 配置本地化支持

Flutter 主题概述

在 Flutter 中,**主题(Theme)**是用来统一管理应用中 UI 样式和配色的机制。它可以让你在整个应用中保持一致的视觉风格,同时方便动态切换(如暗黑模式)。下面我帮你梳理完整的主题体系和使用方法。

1. 主题的核心概念

  1. ThemeData
    • 定义应用的主题数据,包括颜色、字体、图标样式、按钮样式等。
    • 常用属性:
      • primaryColor:主色调
      • accentColor(Flutter 2 以前)或 colorScheme.secondary:强调色
      • brightness:亮/暗模式
      • textTheme:文本样式
      • iconTheme:图标样式
  2. Theme
    • 一个 Widget,负责将 ThemeData 注入到子树。
    • 常用在整个应用或局部组件中设置主题。
  3. MaterialApp 的 theme / darkTheme / themeMode
    • theme:应用的默认亮色主题
    • darkTheme:应用的暗色主题
    • themeMode:控制当前使用亮/暗主题(ThemeMode.light / dark / system

2. 全局主题示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

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

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 主题示例',
theme: ThemeData(
primarySwatch: Colors.blue, // 主色
brightness: Brightness.light,
textTheme: const TextTheme(
bodyMedium: TextStyle(fontSize: 18, color: Colors.black),
),
),
darkTheme: ThemeData(
primarySwatch: Colors.blue,
brightness: Brightness.dark,
textTheme: const TextTheme(
bodyMedium: TextStyle(fontSize: 18, color: Colors.white),
),
),
themeMode: ThemeMode.system, // 随系统切换亮/暗主题
home: const HomePage(),
);
}
}

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

@override
Widget build(BuildContext context) {
// 获取主题数据
final theme = Theme.of(context);

return Scaffold(
appBar: AppBar(
title: const Text('主题示例'),
),
body: Center(
child: Text(
'Hello Theme',
style: theme.textTheme.bodyMedium,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
}
}

3. 局部主题

  • 有时需要让某个子树使用不同的主题,可以使用 ‘Theme Widget‘:
1
2
3
4
5
6
7
Theme(
data: Theme.of(context).copyWith(
primaryColor: Colors.red,
),
child: SomeWidget(),
)

  • copyWith 可以基于现有主题修改部分属性。

4. 自定义主题样式

  1. 按钮样式(ButtonStyle)
    1
    2
    3
    4
    5
    6
    7
    8
    ElevatedButton(
    style: ElevatedButton.styleFrom(
    backgroundColor: Theme.of(context).primaryColor,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
    ),
    onPressed: () {},
    child: const Text('按钮'),
    )
  2. 图标样式(IconThemeData)
    1
    2
    3
    4
    IconTheme(
    data: IconThemeData(color: Theme.of(context).primaryColor, size: 30),
    child: Icon(Icons.star),
    )
  3. 文本样式(TextTheme)
    • 通过 Theme.of(context).textTheme 获取当前主题的文本样式。

5. 动态切换主题

  • 可以结合 Provider / ValueNotifier / Riverpod 等状态管理实现动态切换主题:

    1
    2
    3
    4
    5
    6
    7
    ThemeMode themeMode = ThemeMode.light; // 用 Provider 或 StateNotifier 管理

    MaterialApp(
    themeMode: themeMode,
    theme: ThemeData.light(),
    darkTheme: ThemeData.dark(),
    )
  • 用户可以通过按钮切换 themeMode


6. 总结

概念 用途
ThemeData 定义主题样式,颜色、字体、图标等
Theme Widget 注入主题到子树,可局部修改
MaterialApp.theme 全局亮色主题
MaterialApp.darkTheme 全局暗色主题
MaterialApp.themeMode 控制当前使用哪个主题
Theme.of(context) 获取当前上下文的主题数据

Flutter 主题体系非常灵活,既能全局统一样式,也能局部覆盖,还能结合状态管理动态切换,实现暗黑模式、个性化配色等效果。

GetX实现多语言和主题切换

现在换成GetX插件提供的国际化功能,非常好用. 在main.dart的build方法里配置getx:

1
2
3
4
5
6
7
8
9
GetMaterialApp(
translations: IntlMsgs(), // 国际化语言包
locale: Locale('zh', 'CN'),
fallbackLocale: Locale('en', 'US'),
localeListResolutionCallback: (locales, supportedLocales) {
print('当前系统语言环境:$locales');
return;
},
...

可以看到MaterialApp替换成了GetMaterialApp,translations参数配置了一个自定义的语言包文件,举个例子:

1
2
3
4
5
6
7
8
9
10
11
class IntlMsgs extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'zh_CN': {
'english': '英文',
},
'en_US': {
'english': 'english',
},
};
}

创建一个类继承自Translations,重写keys的get方法,在里面配置多种语言的字段,上面的代码只配置了一个english字段,支持中文和英文,我们在使用的时候就可以:

1
2
3
Text(
'english'.tr,
),

字符串后面加个 .tr 就行了,getx会根据当前语言环境获取对应的字段

我们在切换语言的时候,只需要:

1
Get.updateLocale(Locale('en_US'));

就切换到英文了,因为getx自带状态管理,我们不需要考虑页面刷新

下面说深色模式,也是只需要一行:

1
2
3
4
5
6
7
8
9
Get.changeTheme(isDark
? ThemeData(
brightness: Brightness.dark,
primaryColor: Utils.hexColor('2c2c2b'),
)
: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.white,
));

可以用Get.isDarkMode来获取当前是否是深色模式