flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题

flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题

1、国际化

1.1、 使用到的插件如下,添加到pubspec.yaml中,vscode会自动下载,你也可以通过命令下载flutter pub get

dart 复制代码
  # 国际化支持
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

1.2、安装编译器插件 Flutter Intl(Android Studio安装也是直接搜就可以,不习惯用Android Studio就懒得试了)。

1.3、执行命令,创建必要文件。

vscode 点击顶部输入框输入命令

输入 >Flutter Intl: Initialize 后点击回车,等待初始化完成

初始化成功会默认创建如下两个文件夹

generated文件夹不需要修改

l10n文件夹默认只有 intl_en.arb 文件,即默认英文

通过命令添加其它语言 >Flutter Intl: Add locale,回车继续输入 zh_CN回车确认即可

执行完命令就会创建新的 arb文件

1.4、使用

  1. .arb文件内容,.arb文件内容和 json 格式一致。以 key:value 的形式存储。
  2. 在需要使用的地方通过 S.of(context).home 即可拿到,注意导入相关的包和确保 context 能安全的拿到。
json 复制代码
{
    "home": "Home",
    "setting": "Setting",
    "video": "Video",
    "food": "Food",
    "internationalization": "Internationalization",
    "theme": "Theme",
    "icon": "Icon",
    "icons": "Icons",
    "about": "About",
    "login": "Login"
}

1.5、结合 shared_preferences 持久化存储和 provider 状态管理实现持久化和实时更新。

  • 创建 localization_info.dart 文件:
  • provider用法可以去官网查一下,视频播放器UI封装时就介绍过了,这里就懒得介绍,直接看实现。
dart 复制代码
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class LocalizationInfo with ChangeNotifier {
  String _localeKey = 'zh_CN';
  String get localeKey => _localeKey;
  
  // 初始化
  LocalizationInfo() {
    initLanguage();
  }

  // 设置语言
  void setLocaleKey(String locale) async {
    final prefs = await SharedPreferences.getInstance();
    _localeKey = locale;
    prefs.setString('_localeKey', locale);
    notifyListeners(); // 通知监听者
  }

  // 初始化语言
  void initLanguage() async {
    final prefs = await SharedPreferences.getInstance();
    _localeKey = prefs.getString('_localeKey') ?? 'zh_CN';
    notifyListeners();
  }
}
  • 注入 provider,在main.dart中注入。
  • 同时,直接在 main.dart中使用。
  • _getLocale方法通过不同的值返回不一样的 Locale。
  • _getLocale封装的比较简单粗暴,可自行封装成通用的方法。
dart 复制代码
  Locale _getLocale(String localeKey) {
    switch (localeKey) {
      case 'zh_CN':
        return const Locale('zh', 'CN');
      case 'en':
        return const Locale('en');
      default:
        return const Locale('zh', 'CN');
    }
  }
  • 创建一个localizations_page.dart页面来设置语言
dart 复制代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wallpaper/components/appbar_base.dart';
import 'package:wallpaper/generated/l10n.dart';
import 'package:wallpaper/themes/localization_info.dart';

class LocalizationsPage extends StatefulWidget {
  const LocalizationsPage({super.key});

  @override
  State<LocalizationsPage> createState() => _LocalizationsPageState();
}

// ignore: camel_case_types
class localItem {
  final String title;
  final String localeKey;
  const localItem(this.title, this.localeKey);
}

class _LocalizationsPageState extends State<LocalizationsPage> {
  @override
  Widget build(BuildContext context) {
    final localization = Provider.of<LocalizationInfo>(context);
    List<localItem> localList = [
      const localItem('中文', 'zh_CN'),
      const localItem('English', 'en'),
    ];
    return Scaffold(
      appBar: AppbarBase(title: S.of(context).internationalization),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Flex(
          direction: Axis.vertical,
          spacing: 8,
          children: [
            for (var item in localList)
              Material(
                  color: Theme.of(context).colorScheme.primaryContainer,
                  borderRadius: BorderRadius.circular(8),
                  clipBehavior: Clip.antiAlias,
                  child: ListTile(
                    title: Text(item.title),
                    // 右侧图标
                    trailing: localization.localeKey == item.localeKey
                        ? Icon(
                            Icons.check,
                            color: Theme.of(context).colorScheme.primary,
                          )
                        : null,
                    onTap: () {
                      localization.setLocaleKey(item.localeKey);
                    },
                  )),
          ],
        ),
      ),
    );
  }
}
  • 只要通过 localization.setLocaleKey() 设置语言就会通知 main.dart 重新加载语言,并且会做持久化处理,下次打开软件时会自动加载设置好的语言。

2、主题

  • 主题和国际化配置基本上如出一辙,都是切换、持久存储、实时更新。
  • 使用到的插件也都是 shared_preferencesprovider , 这两个非常好用和简单、建议学一下。

自定义主题文件 theme.dartcolorScheme还有很多属性都能自定义,可以自己查一下,基本上下面这些属性已经完全够用了,我们可以通过 Theme.of(context).colorScheme.primaryContainer 使用定义好的颜色。

dart 复制代码
import 'package:flutter/material.dart';

ThemeData lightMode = ThemeData(
  brightness: Brightness.light,
  colorScheme: ColorScheme.light(
    surface: Colors.grey.shade200,
    onSurface: Colors.grey.shade900,
    primary: const Color.fromARGB(255, 0, 94, 255),
    primaryContainer: Colors.white,
    secondary: Colors.grey.shade300,
    inversePrimary: Colors.grey.shade800,
    shadow: const Color.fromARGB(112, 80, 80, 80),
  ),
);

ThemeData darkMode = ThemeData(
  brightness: Brightness.dark,
  colorScheme: ColorScheme.dark(
      surface: Colors.grey.shade900,
      onSurface: Colors.grey.shade100,
      primary: Colors.deepPurpleAccent,
      primaryContainer: const Color.fromARGB(255, 23,23,23),
      secondary: const Color.fromARGB(107, 66, 66, 66),
      inversePrimary: Colors.grey.shade200,
      shadow: const Color.fromARGB(62, 75, 75, 75)),
);

创建 theme_provider.dart 文件设置主题,里面注释已经很详细了,也没啥难的逻辑,我就直接介绍怎么使用了。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wallpaper/themes/theme.dart';

class ThemeProvider extends ChangeNotifier {
  ThemeData _themeData = lightMode;
  bool _followSystem = false;
  bool _isInitialized = false;
  VoidCallback? _brightnessListener;

  ThemeData get themeData => _themeData;
  bool get isDarkMode => _themeData == darkMode;
  bool get followSystem => _followSystem;

  ThemeProvider() {
    _initTheme();
  }

  // 初始化主题(异步)
  Future<void> _initTheme() async {
    if (_isInitialized) return;

    final prefs = await SharedPreferences.getInstance();
    _followSystem = prefs.getBool('followSystem') ?? false;

    if (_followSystem) {
      _themeData = _getSystemTheme();
    } else {
      final isDark = prefs.getBool('isDark') ?? false;
      _themeData = isDark ? darkMode : lightMode;
    }

    _addSystemThemeListener();
    _isInitialized = true;
    notifyListeners();
  }

  // 系统主题监听
  void _addSystemThemeListener() {
    _brightnessListener = () {
      if (_followSystem) {
        _themeData = _getSystemTheme();
        notifyListeners();
      }
    };

    WidgetsBinding.instance.platformDispatcher.onPlatformBrightnessChanged =
        _brightnessListener;
  }

  // 清理资源
  @override
  void dispose() {
    WidgetsBinding.instance.platformDispatcher.onPlatformBrightnessChanged =
        null;
    super.dispose();
  }

  // 统一保存配置
  Future<void> _savePreferences() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('isDark', isDarkMode);
    await prefs.setBool('followSystem', _followSystem);
  }

  // 切换主题
  void toggleTheme() {
    _followSystem = false;
    _themeData = isDarkMode ? lightMode : darkMode;
    _savePreferences();
    notifyListeners();
  }

  // 设置浅色主题
  void setLightTheme() {
    _followSystem = false;
    _themeData = lightMode;
    _savePreferences();
    notifyListeners();
  }

  // 设置深色主题
  void setDarkTheme() {
    _followSystem = false;
    _themeData = darkMode;
    _savePreferences();
    notifyListeners();
  }

  // 跟随系统主题
  void setFollowSystem() {
    _followSystem = true;
    _themeData = _getSystemTheme();
    _savePreferences();
    notifyListeners();
  }

  // 获取系统主题
  ThemeData _getSystemTheme() {
    final brightness =
        WidgetsBinding.instance.platformDispatcher.platformBrightness;
    return brightness == Brightness.dark ? darkMode : lightMode;
  }
}
  • main.dart 中注入ThemeProvider

  • 直接在 main.dart 中使用主题

  • 因为上面国际化时已经使用了 Consumer,设置主题可以采用 Provider.of<ThemeProvider>(context).themeData的方式直接设置,详细用法见官网。

  • 创建一个theme_page.dart页面来设置主题

dart 复制代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wallpaper/components/appbar_base.dart';
import 'package:wallpaper/themes/theme_provider.dart';

class ThemePage extends StatefulWidget {
  const ThemePage({super.key});

  @override
  State<ThemePage> createState() => _ThemePageState();
}

class _ThemePageState extends State<ThemePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppbarBase(title: '主题设置'),
        body: Consumer<ThemeProvider>(builder: (context, provider, child) {
          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: Flex(
              direction: Axis.vertical,
              spacing: 10,
              children: [
                SizedBox(
                  child: Material(
                    borderRadius: BorderRadius.circular(10),
                    clipBehavior: Clip.antiAlias,
                    color: Theme.of(context).colorScheme.primaryContainer,
                    child: InkWell(
                      onTap: () {
                        provider.setLightTheme();
                      },
                      child: Row(
                        children: [
                          Radio(
                              value: !provider.isDarkMode &&
                                  !provider.followSystem,
                              groupValue: true,
                              onChanged: (value) {
                                provider.setLightTheme();
                              }),
                          Text('浅色模式')
                        ],
                      ),
                    ),
                  ),
                ),
                Material(
                  borderRadius: BorderRadius.circular(10),
                  clipBehavior: Clip.antiAlias,
                  color: Theme.of(context).colorScheme.primaryContainer,
                  child: InkWell(
                    onTap: () {
                      provider.setDarkTheme();
                    },
                    child: Row(
                      children: [
                        Radio(
                            value:
                                provider.isDarkMode && !provider.followSystem,
                            groupValue: true,
                            onChanged: (value) {
                              provider.setDarkTheme();
                            }),
                        Text('暗色模式')
                      ],
                    ),
                  ),
                ),
                Material(
                  borderRadius: BorderRadius.circular(10),
                  clipBehavior: Clip.antiAlias,
                  color: Theme.of(context).colorScheme.primaryContainer,
                  child: InkWell(
                    onTap: () {
                      provider.setFollowSystem();
                    },
                    child: Row(
                      children: [
                        Radio(
                            value: provider.followSystem,
                            groupValue: true,
                            onChanged: (value) {
                              provider.setFollowSystem();
                            }),
                        Text('跟随系统')
                      ],
                    ),
                  ),
                ),
              ],
            ),
          );
        }));
  }
}
  • 同理只要设置了主题就会实时更新和存储主题,下次打开时自动读取存储的主题。

3、视频播放器UI

视频播放器UI,没啥大的变化,稍微调整了边距。

4、扫码功能

扫码功能,美化UI,简单添加提示和刷新功能。

5、水波纹问题

问题:

  1. 不生效问题。
  2. 跳转页面后,水波纹动画不会继续执行,需返回页面后才会执行执行,体验非常糟糕。

解决:

  1. InkWell 举例,水波纹需要 Material 或者 Scaffold 来承载,如果使用 Container 等包裹,水波纹可能就会失效。
  2. 通过自定义跳转动画使水波纹能够执行。可以自定义跳转动画,并且水波纹不会停止播放,非常nice。
dart 复制代码
  Navigator.push(
     context,
     PageRouteBuilder(
       settings: RouteSettings(name: ''), // 保留路由名称
       pageBuilder: (_, animation, __) =>
           list[i].route!, // 替换为你的目标页面
       transitionsBuilder: (_, animation, __, child) {
         // 示例:从右向左滑动动画
         return SlideTransition(
           position: Tween<Offset>(
             begin: const Offset(1.0, 0.0),
             end: Offset.zero,
           ).animate(CurvedAnimation(
             parent: animation,
             curve: Curves.easeInOut,
           )),
           child: child,
         );
       },
       transitionDuration:
           const Duration(milliseconds: 300), // 动画时长
     ),
   );

6、项目地址

gitee.com/zsnoin-can/...

  • 使用的是本地 gradle-8.3-all.zip
相关推荐
奋斗的小青年!!2 小时前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘5 小时前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!9 小时前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨10 小时前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者9611 小时前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨13 小时前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei13 小时前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei13 小时前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!13 小时前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_13 小时前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter