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
相关推荐
张风捷特烈4 小时前
Flutter 伪3D绘制#03 | 轴测投影原理分析
android·flutter·canvas
马拉萨的春天7 小时前
flutter 项目结构目录以及pubspec.ymal等文件描述
flutter
bst@微胖子1 天前
Flutter项目之登录注册功能实现
开发语言·javascript·flutter
小墙程序员1 天前
Flutter 教程(十一)多语言支持
flutter
无知的前端1 天前
Flutter 一文精通Isolate,使用场景以及示例
android·flutter·性能优化
yidahis1 天前
Flutter 运行新建项目也报错?
flutter·trae
木马不在转1 天前
Flutter-权限permission_handler插件配置
flutter
江上清风山间明月1 天前
一周掌握Flutter开发--9. 与原生交互(下)
flutter·交互·原生·methodchannel
GeniuswongAir1 天前
Flutter极速接入IM聊天功能并支持鸿蒙
flutter·华为·harmonyos
sayen1 天前
记录 flutter 文本内容展示过长优化
前端·flutter