Flutter应用服务插件app_service的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter应用服务插件app_service的使用

1. 获取开始指南

你可以通过以下命令在项目中安装最新版本的App Service

flutter pub add app_service

这将在你的项目的pubspec.yaml文件的dependencies字段中添加app_service作为依赖项,并隐式运行flutter pub get

2. 在依赖注入中管理App Service

在实际项目中,除了AppService外,可能还需要管理许多其他依赖项。因此,我喜欢创建一个injections.dart文件来描述这些依赖项。以下示例使用了Get库来管理依赖项。

import 'package:app_service/app_service.dart';
import 'package:get/instance_manager.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> initDependencies() async {
  final SharedPreferences prefs = await SharedPreferences.getInstance();
  Get.put<SharedPreferences>(prefs);

  // 应用管理
  Get.lazyPut<AppService>(
    () => AppService(
      Get.find<SharedPreferences>(),
      supportedLanguages: const [
        LanguageEnum.zh,
        LanguageEnum.zhHk,
        LanguageEnum.zhMO,
        LanguageEnum.zhTW,
        LanguageEnum.en,
        LanguageEnum.enUK,
        LanguageEnum.enUS,
        LanguageEnum.de,
        LanguageEnum.ru,
        LanguageEnum.uk,
        LanguageEnum.be,
        LanguageEnum.kk,
        LanguageEnum.sr,
        LanguageEnum.fr,
        LanguageEnum.ja,
        LanguageEnum.ko,
        LanguageEnum.ar,
      ],
    ),
    fenix: true,
  );
}

在旧版本中,你需要使用defaultLang: LanguageEnum.zh,配置默认语言。目前,列表中的第一个配置作为默认值。

2.1 主题管理

主题管理用于在不同的颜色主题之间切换,每个主题包括两种模式:深色模式和浅色模式。该库包含了许多由flex_color_scheme生成的内置主题数据。

以下是内置主题的表格:

主题名称 浅色主题 深色主题
amberBlue amberBlueLightTheme amberBlueDarkTheme
aquaBlue aquaBlueLightTheme aquaBlueDarkTheme
bahamaTrinidad bahamaTrinidadLightTheme bahamaTrinidadDarkTheme
barossa barossaLightTheme barossaDarkTheme
bigStoneTulip bigStoneTulipLightTheme bigStoneTulipDarkTheme
blueDelight blueDelightLightTheme blueDelightDarkTheme
blueStoneTeal blueStoneTealLightTheme blueStoneTealDarkTheme
blueWhale blueWhaleLightTheme blueWhaleDarkTheme
blumine blumineLightTheme blumineDarkTheme
brandBlue brandBlueLightTheme brandBlueDarkTheme
brownOrange brownOrangeLightTheme brownOrangeDarkTheme
camaroneGreen camaroneGreenLightTheme camaroneGreenDarkTheme
damaskLunar damaskLunarLightTheme damaskLunarDarkTheme
deepBlueSea deepBlueSeaLightTheme deepBlueSeaDarkTheme
deepPurple deepPurpleLightTheme deepPurpleDarkTheme
dellGenoaGreen dellGenoaGreenLightTheme dellGenoaGreenDarkTheme
ebonyClay ebonyClayLightTheme ebonyClayDarkTheme
eggplantPurple eggplantPurpleLightTheme eggplantPurpleDarkTheme
endeavourBlue endeavourBlueLightTheme endeavourBlueDarkTheme
espressoCrema espressoCremaLightTheme espressoCremaDarkTheme
flutterDash flutterDashLightTheme flutterDashDarkTheme
goldSunset goldSunsetLightTheme goldSunsetDarkTheme
greens greensLightTheme greensDarkTheme
greenForest greenForestLightTheme greenForestDarkTheme
greenJungle greenJungleLightTheme greenJungleDarkTheme
greenMoney greenMoneyLightTheme greenMoneyDarkTheme
greyLaw greyLawLightTheme greyLawDarkTheme
hippieBlue hippieBlueLightTheme hippieBlueDarkTheme
indigoNight indigoNightLightTheme indigoNightDarkTheme
indigoSanMarino indigoSanMarinoLightTheme indigoSanMarinoDarkTheme
lipstickPink lipstickPinkLightTheme lipstickPinkDarkTheme
mallardValencia mallardValenciaLightTheme mallardValenciaDarkTheme
mangoMojito mangoMojitoLightTheme mangoMojitoDarkTheme
material3 material3LightTheme material3DarkTheme
material3HighContrast material3HighContrastLightTheme material3HighContrastDarkTheme
material3Purple material3PurpleLightTheme material3PurpleDarkTheme
midnight midnightLightTheme midnightDarkTheme
mosqueCyan mosqueCyanLightTheme mosqueCyanDarkTheme
ohMandyRed ohMandyRedLightTheme ohMandyRedDarkTheme
outerSpace outerSpaceLightTheme outerSpaceDarkTheme
pinkSakura pinkSakuraLightTheme pinkSakuraDarkTheme
purpleBrown purpleBrownLightTheme purpleBrownDarkTheme
redBlue redBlueLightTheme redBlueDarkTheme
redTornado redTornadoLightTheme redTornadoDarkTheme
redWine redWineLightTheme redWineDarkTheme
rosewood rosewoodLightTheme rosewoodDarkTheme
rustDeepOrange rustDeepOrangeLightTheme rustDeepOrangeDarkTheme
sanJuanBlue sanJuanBlueLightTheme sanJuanBlueDarkTheme
sharkOrange sharkOrangeLightTheme sharkOrangeDarkTheme
thunderbirdRed thunderbirdRedLightTheme thunderbirdRedDarkTheme
verdunGreen verdunGreenLightTheme verdunGreenDarkTheme
verdunLime verdunLimeLightTheme verdunLimeDarkTheme
vesuviusBurned vesuviusBurnedLightTheme vesuviusBurnedDarkTheme
willowWasabi willowWasabiLightTheme willowWasabiDarkTheme
yukonGoldYellow yukonGoldYellowLightTheme yukonGoldYellowDarkTheme

可以通过AppService实例对象的setColorTheme方法切换主题,其类型签名如下:

void setColorTheme(ColorThemesEnum themeEnum)

例如:

// 获取AppService实例
final AppService appService = GetIt.instance.get<AppService>();

// 切换主题为bigStoneTulip
appService.setColorTheme(ColorThemesEnum.bigStoneTulip);
ThemeModal

你可以使用ThemeModal模态对话框组件为用户提供更直观的主题选择,例如:

const ThemeModal(),

它会以主题图标的形式显示在页面上:

chrome_HM2hFfct9z

如果触摸或点击此图标,则会出现一个对话框供用户选择主题:

chrome_oPKRHK21u2

每个主题将以其primaryColor的圆形形状显示在模态对话框中,并且选中的主题会有勾选标记。

从版本3.0.0开始,你可以在ThemeModal组件中通过themes参数指定可用的主题。如果没有指定或者指定为空数组,则默认使用所有内置主题。

showThemeModal

showThemeModal是一个比ThemeModal更具灵活性的功能。例如:

// 某个组件的onTap参数
onTap: (_) {
  showThemeModal(
    context,
    themes: [
      ColorThemesEnum.amberBlue,
      ColorThemesEnum.brownOrange,
      ColorThemesEnum.dellGenoaGreen,
      ColorThemesEnum.camaroneGreen,
    ],
  );
},
2.2 暗色模式管理

App Service库中,暗色/亮色模式是同一主题下的两个子状态,本质上定义了两组相应的主题数据。你可以直接通过AppService单例中的toggleDarkMode方法切换暗色模式:

// 获取AppService的单例实例
final AppService appService = GetIt.instance.get<AppService>();
// 切换暗色模式
appService.toggleDarkMode()

此外,在appService实例对象上还有setDarkModesetLightMode方法用于设置暗色/亮色模式。

DarkModeSwitch是一个可以直接使用的开关,用于切换暗色模式。例如:

chrome_kVi5w711Re

2.3 跟随系统模式

从V4.0.0版本开始,添加了一个新的功能:跟随系统的暗色模式。通过更改AppService中的followSystem属性的值,可以设置是否跟随系统。一旦手动更改了暗色模式,followSystem的值将被设置为false。followSystem是一个RxBool类型的变量,意味着它是响应式的。例如:

Obx(
  () => Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      Text('app_service.follow_system'.tr + 'app_service.:'.tr),
      Checkbox(
        value: Get.find<AppService>().followSystem.value,
        onChanged: (value) {
          Get.find<AppService>().followSystem.value = value!;
          Get.find<AppService>().saveFollowSystem();
        },
      ),
    ],
  ),
),

alt text

系统模式跟随的行为因平台而异;在Windows平台上,它会强制跟随系统的暗色模式。

3. 本地化

3.1 消息

Messages是一个接受多个翻译的翻译容器。它的类型签名如下:

Messages Messages(List<Map<String, Map<String, String>>> translations)

你应该将其通过translations参数传递给顶级组件GetMaterialApp,并且必须包括AppServiceMessages().keys,这是App Service的一个翻译文件。例如:

GetMaterialApp(
  translations: Messages([
    AppServiceMessages().keys,
    // 其他翻译
    HomeMessages().keys,
  ]),
);

其中HomeMessages是一个假设的自定义翻译文件,看起来像这样:

import 'package:get/get.dart';

class HomeMessages extends Translations {
  [@override](/user/override)
  Map<String, Map<String, String>> get keys => {
        'zh_CN': {
          'home.appService_demo': 'AppService 演示',
        },
        'en': {
          'home.appService_demo': 'AppService Demo',
        },
        'ko_KR': {
          'home.appService_demo': 'AppService 데모',
        },
        'ja_JP': {
          'home.appService_demo': 'AppService デモ',
        },
        // 更多语言翻译...
      };
}

当然,根据项目的需要,你可以定义更多这样的翻译文件并加载到Messagestranslations列表中。

在实现国际化时,通过AppService构造函数的supportedLanguages参数指定支持的语言。它接受一个由多个LanguageEnum枚举值组成的列表。此外,AppService需要指定一个默认语言。例如:

AppService appService = AppService(
  supportedLanguages: const [
    LanguageEnum.zh,
    LanguageEnum.en,
    LanguageEnum.fr,
  ],
  defaultLang: LanguageEnum.zh,
);

defaultLang的默认值是LanguageEnum.en。此定义需要与顶级组件中的locale参数相对应。

应用程序标题不能使用GetX.tr进行翻译,因为在顶级组件初始化之前此方法不可用。这种限制在Web上的本地化切换效果尤为明显:

chrome_0ywly93CSl

为了实现这种动态切换,你可以使用switch语句,如以下示例所示:

import 'package:app_service/app_service.dart';

// ...
GetMaterialApp(
  // ...
  title: switch (Get.locale?.languageCode) {
    'zh' => 'AppService 演示',
    'en' => 'AppService Demo',
    'fr' => 'AppService démonstration',
    'ja' => 'AppServiceデモ',
    'ko' => 'App 서비스 데모',
    'ar' => 'تطبيق AppService',
    _ => 'AppService Demo',
  },
  translations: Messages([
    AppServiceMessages().keys,
    HomeMessages().keys,
  ]),
  locale: Locale(appService.currentLangStr),
  home: const HomeView(),
);

要切换语言,可以使用AppService实例对象上的updateLocale方法,其类型签名如下:

void updateLocale(LanguageEnum newLanguage)

例如:

appService.updateLocale(LanguageEnum.zh);
3.2 本地语言切换

有两种可用的Widget用于显示语言选择菜单以切换本地语言:LangSelectMenuWen

3.2.1 LangSelectMenu

LangSelectMenu是一个普通的矩形下拉按钮,例如:

const LangSelectMenu(),

它看起来像这样:

chrome_WBdxDZiVCG

Wen也是一个弹出菜单的按钮,但它显示一个图标,通常在Header中使用:

const Wen()

它看起来像这样:

C844qQlH1K

你可以自定义显示的图标及其大小,它可以是任何小部件。

LanguageSelectPage & CupertinoLanguageSelectPage

如果你想在一个设置页面中选择语言,可以考虑使用LanguageSelectPageCupertinoLanguageSelectPage组件。这些组件代表一个语言选择页面,可以从设置项中打开。

1709576847626

3. Web 应用

在Web应用中,当前的sharedPreferencesWeb库通过localStorage实现键值对存储。AppService管理的相关状态的变化会直接反映在浏览器的localStorage中:

Alt text

4. 示例应用

你可以在这里找到App Service的一个示例应用。

示例代码

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:app_service/app_service.dart';

import 'app/injections.dart';

import 'app/router.dart';
import 'modules/home/home_view.dart';
import 'modules/home/home_tr.dart';

void main() async {
  Get.testMode = true;

  WidgetsFlutterBinding.ensureInitialized();
  Get.testMode = kDebugMode;

  await initDependencies();

  runApp(const MyApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    final appService = Get.find<AppService>();

    return GetMaterialApp(
      debugShowCheckedModeBanner: Get.testMode,
      title: switch (Get.locale?.toLanguageTag()) {
        'zh' => 'AppService 演示 (zh)',
        'zh-CN' => 'AppService 演示(陆)',
        'zh-HK' => 'AppService 演示(港)',
        'zh-MO' => 'AppService 演示(澳)',
        'zh-TW' => 'AppService 演示(台)',
        'en' => 'AppService Demo (en)',
        'en-GB' => 'AppService Dem (GB)',
        'en-US' => 'AppService Demo (US)',
        'ru' => 'Демонстрация сервиса приложения',
        'ru-RU' => 'Демонстрация сервиса приложения',
        'ru-BY' => 'Дэма-версія паслугі',
        'ru-UA' => 'Демо-версія служби',
        'ru-KZ' => 'AppService Демо',
        'fr' => 'AppService démonstration (fr)',
        'ja' => 'AppServiceデモ (日)',
        'ko' => 'App 서비스 데모',
        'ar' => 'تطبيق AppService',
        _ => 'AppService Demo',
      },
      theme: appService.currentTheme,
      getPages: AppRoutes.routes,
      translations: Messages([
        AppServiceMessages().keys,
        HomeMessages().keys,
      ]),
      locale: Locale(appService.currentLangStr),
      fallbackLocale: const Locale('en', 'US'),
      home: const HomeView(),
    );
  }
}

更多关于Flutter应用服务插件app_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter应用服务插件app_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,app_service 插件允许你与原生平台(Android 和 iOS)的服务进行交互。虽然 Flutter 生态系统中有许多插件用于不同的目的,但 app_service 插件可能并不是一个广泛认知的标准插件,通常我们可能会通过 MethodChannel 或其他类似机制来实现与原生服务的交互。不过,为了演示如何在 Flutter 中与原生服务进行交互,我将展示一个使用 MethodChannel 的例子,这是 Flutter 中与原生平台通信的一种常用方法。

Flutter 端代码

首先,在你的 Flutter 项目中,创建一个新的 Dart 文件,比如 AppService.dart,用于封装与原生平台的通信逻辑。

import 'package:flutter/services.dart';

class AppService {
  static const MethodChannel _channel = MethodChannel('com.example.app_service');

  // 调用原生服务的方法
  Future<String?> callNativeService(String methodName, Map<String, dynamic> arguments) async {
    try {
      final result = await _channel.invokeMethod(methodName, arguments);
      return result as String?;
    } on PlatformException catch (e) {
      print("Failed to invoke: '${e.message}'.");
      return null;
    }
  }
}

然后,在你的 Flutter 应用中,你可以像这样使用这个服务:

import 'package:flutter/material.dart';
import 'AppService.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter App Service Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              final appService = AppService();
              final result = await appService.callNativeService('someMethodName', {
                'key1': 'value1',
                'key2': 123,
              });
              print("Native service result: $result");
            },
            child: Text('Call Native Service'),
          ),
        ),
      ),
    );
  }
}

原生端代码

Android 端

在你的 MainActivity.ktMainActivity.java 文件中,设置 MethodChannel 并处理来自 Flutter 的调用。

Kotlin:

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.app_service"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "someMethodName") {
                val arguments = call.arguments as? Map<String, Any>
                val key1 = arguments?.get("key1") as? String
                val key2 = arguments?.get("key2") as? Int

                // 处理你的业务逻辑
                val response = "Processed $key1 and $key2"

                result.success(response)
            } else {
                result.notImplemented()
            }
        }
    }
}

Java:

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import android.os.Bundle;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.example.app_service";

    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                            if (call.method.equals("someMethodName")) {
                                Map<String, Object> arguments = (Map<String, Object>) call.arguments;
                                String key1 = (String) arguments.get("key1");
                                Integer key2 = (Integer) arguments.get("key2");

                                // 处理你的业务逻辑
                                String response = "Processed " + key1 + " and " + key2;

                                result.success(response);
                            } else {
                                result.notImplemented();
                            }
                        }
                );
    }
}

iOS 端

在你的 AppDelegate.swiftAppDelegate.m 文件中,设置 MethodChannel 并处理来自 Flutter 的调用。

Swift:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.app_service", binaryMessenger: controller)
    channel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      if call.method == "someMethodName" {
        if let arguments = call.arguments as? [String: Any],
           let key1 = arguments["key1"] as? String,
           let key2 = arguments["key2"] as? Int {
          // 处理你的业务逻辑
          let response = "Processed \(key1) and \(key2)"
          result(response)
        } else {
          result(FlutterError(code: "UNIMPLEMENTED", message: "Method not implemented.", details: nil))
        }
      } else {
        result(FlutterError(code: "UNKNOWN_METHOD", message: "Unknown method.", details: nil))
      }
    })
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Objective-C:

#import "AppDelegate.h"
#import <Flutter/Flutter.h>

@interface AppDelegate ()
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
  FlutterMethodChannel* channel = [FlutterMethodChannel
                                   methodChannelWithName:@"com.example.app_service"
                                   binaryMessenger:controller];
  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
    if ([@"someMethodName" isEqualToString:call.method]) {
      NSDictionary *arguments = call.arguments;
      NSString *key1 = arguments[@"key1"];
      NSNumber *key2 = arguments[@"key2"];

      // 处理你的业务逻辑
      NSString *response = [NSString stringWithFormat:@"Processed %@ and %@", key1, key2];
      result(response);
    } else {
      result([FlutterError errorWithCode:@"UNKNOWN_METHOD"
                                 message:@"Unknown method."
                                 details:nil]);
    }
  }];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

这个示例展示了如何通过 MethodChannel 在 Flutter 与原生平台(Android 和 iOS)之间进行通信。如果你确实有一个特定的 app_service 插件,并且它提供了不同的接口或配置方式,请参考该插件的官方文档或示例代码进行实现。

回到顶部