Flutter即时通讯插件flutter_jimtl的使用

Flutter即时通讯插件flutter_jimtl的使用

如果你对手动编写ARB文件感到厌倦,或者觉得Flutter本地化支持有限,那么你来对地方了。

通过这个包,你可以:

  • 从Dart代码生成ARB文件(由build_runner完成)
  • 远程更新你的翻译(来自外部服务器或翻译服务)
  • 为你的翻译提供不同的风味

设置

安装最新的jimtl版本用于Dart,flutter_jimtl用于Flutter,以及jimtl_codegen

dependencies:
  intl:
  flutter_jimtl:

dev_dependencies:
  build_runner: ^2.0.3
  jimtl_codegen:

使用intl包的强大功能进行翻译

intl是一个处理翻译的好工具包,它允许你以简单且安全的方式编写消息、复数和性别。

让我们来看一些基本的例子:

@GenerateArb()
class Translations {
  // 仅在flutter项目中使用
  static Translations of(BuildContext context) => Localizations.of<Translations>(context, Translations)!;

  String get counter => Intl.message('Counter', name: 'counter');

  String get increment => Intl.message('Increment', name: 'increment');

  String counterPushed(int number) => Intl.message('You have pushed the button $number times: ', args: [number], name: 'counterPushed');
}

你是否注意到@GenerateArb()注解?这就是魔法所在。一旦你运行你喜欢的build_runner命令,它将生成与此类对应的ARB文件。

这个注解可以自定义以下字段:

  • defaultLocale: 你正在使用的区域设置,默认为’en’
  • defaultFlavor: 项目的默认风味,默认为’default’
  • suppressMetaData: 生成ARB文件时是否抑制元数据,默认为false
  • suppressLastModified: 生成ARB文件时是否抑制最后修改时间,默认为false
  • includeSourceText: 是否在消息中包含源文本,默认为false
  • dir: 应该生成ARB文件的目录

Flutter项目

如果你有一个带有本地化支持的现有Flutter项目,那么你需要在应用中声明一个LocalizationDelegate

这应该是什么样子:

localizationsDelegates: [
  DefaultMaterialLocalizations.delegate,
  TranslationsDelegate<Translations>(
    defaultLocale: 'en',
    supportedLocales: ['en', 'fr'],
    // 如果你正在使用风味,你需要指定默认和当前风味
    defaultFlavor: 'default',
    currentFlavor: 'flavor1',
    // 此方法用于加载默认的ARB文件,最简单的方法是从资产加载它们
    dataLoader: (locale, flavor) async {
      print('local load $locale and $flavor');
      if (flavor == 'default') {
        return await rootBundle.loadString('assets/arb/translations_$locale.arb');
      }
      return await rootBundle.loadString('assets/arb/translations_${flavor}_$locale.arb');
    },
    // 如果你想从远程服务器下载你的ARB文件
    // 你需要指定一个自定义的数据加载器
    updateDataLoader: (locale, flavor) async {
      print('Remote load $locale and $flavor');
      if (locale == 'en' && flavor == 'flavor1') {
        await Future.delayed(Duration(seconds: 10)); // 模拟一些缓慢的网络响应
        return await rootBundle.loadString('assets/arb/translations_remote_$locale.arb');
      }
      return null;  // 无更新
    },
    // 一旦你的翻译从远程文件更新,此回调将被触发,你需要重建才能看到更改
    onTranslationsUpdated: () {
      print('TX updated, need rebuild');
      setState(() {});
    },
    // 构建器以提供包含翻译的消息的自定义类
    translationsBuilder: () => Translations(),
  ),
]

以下是TranslationsDelegate参数的一些详细信息:

  • defaultLocale: 你应用中要使用的默认区域设置。
  • defaultFlavor: 你应用中可选的默认风味,默认为’default’。
  • currentFlavor: 你应用中可选的当前风味,默认为’default’。
  • overrideCurrentLocale: 可选的应用中要使用的区域设置,它将覆盖系统区域设置。
  • dataLoader: 用于获取给定区域设置和风味的ARB文件的回调,这是由jimtl调用的,让你在需要时提供ARB文件。
  • updateDataLoader: 可选的,为了保持翻译从远程服务器更新,你需要这个回调。它应该返回给定区域设置和风味的ARB内容或如果不需要更新则返回null。
  • onTranslationsUpdated: 可选的,一旦翻译通过updateDataLoader从远程文件更新,这个回调会被调用以便你重建你的小部件并看到更改。
  • supportedLocales: 你的应用支持的区域设置。
  • translationsBuilder: 提供包含翻译的消息的自定义类的构建器。

风味特定性

风味支持是这个包的可选项,但如果你需要它,它在这里:)

风味的ARB文件不必包含应用程序的所有句子,如果某个句子在风味的ARB中不存在,则会使用默认区域设置中的句子。

比如说我们有这个:

默认 => helloWorld => Hello World 风味1 => helloWorld => World Hello 风味2 =>

如果你使用风味1调用myClass.helloWorld,你会得到World Hello 如果你使用风味2调用myClass.helloWorld,你会得到Hello World(默认值)

对于区域设置也是一样的,如果当前区域设置中不存在某个句子,则会使用默认区域设置中的句子。

纯Dart项目

如果你正在使用Dart而不是Flutter,你仍然可以使用这个包,并获得它带来的所有功能!

首先导入依赖项:

dependencies:
  intl:
  jimtl:

dev_dependencies:
  build_runner: ^2.0.3
  jimtl_codegen:

然后你会做相同的自定义类并以相同的方式生成ARB文件。但是Dart没有并且不需要LocalizationDelegate。 但不用担心,这里是如何设置你的翻译的。

final delegate = IntlDelegate(
  // 默认要使用的区域设置
  defaultLocale: 'en',
  // 默认要使用的风味
  defaultFlavor: 'flavor1',
  // 回调以加载给定区域设置和风味的ARB内容
  dataLoader: (String locale, String flavor) async {
    if (flavor == 'default') {
      return await File('./lib/arb/main_$locale.arb').readAsString();
    }
    return await File('./lib/arb/main_${flavor}_$locale.arb').readAsString();
  },
);
// 加载当前区域设置和可选的风味
await delegate.load('fr', currentFlavor: 'flavor1');

一旦load完成,你就可以按你想要的方式使用你的自定义类。

IntlDelegate可以通过以下参数进行自定义:

  • defaultLocale: 你应用中要使用的默认区域设置。
  • defaultFlavor: 你应用中可选的默认风味,默认为’default’。
  • currentFlavor: 你应用中可选的当前风味,默认为’default’。
  • dataLoader: 用于获取给定区域设置和风味的ARB文件的回调,这是由jimtl调用的,让你在需要时提供ARB文件。
  • updateDataLoader: 可选的,为了保持翻译从远程服务器更新,你需要这个回调。它应该返回给定区域设置和风味的ARB内容或如果不需要更新则返回null。
  • supportedLocales: 你的应用支持的区域设置。

从ARB生成Dart代码而不是使用IntlDelegate

你可能希望像intl_translation提供的一样,即将ARB文件生成为Dart代码。

这可以通过这个包实现,但为此,你需要使用GenerateIntl而不是@GenerateArb注解。

GenerateIntl将首先从Dart代码生成ARB文件,然后为每个区域设置和风味生成一些Dart文件。

一旦生成完成,你可以像这样设置这些生成的翻译:

Intl.defaultLocale = 'en';
await initializeMessages(Intl.defaultLocale!, 'flavor');

之后,你可以按你想要的方式使用你的自定义类。

你可以自定义GenerateIntl注解的以下字段:

  • baseFileName: 指定生成文件的基本文件名,默认为带注解的类的名称
  • arbDir: 生成ARB文件的目录,相对于当前文件,默认为’.’
  • genDir: 生成Dart文件的目录,相对于当前文件,默认为’.’
  • defaultLocale: 你正在工作的区域设置,默认为’en’
  • defaultFlavor: 你的项目的默认风味,默认为’default’
  • locales: 你的项目的受支持区域设置列表
  • flavors: 你的项目的风味列表
  • codegenMode: 传递给底层intl_generator的模式,默认为’release’
  • useDeferredLoading: 是否使用延迟加载定位文件,默认为true
  • arbSuppressMetaData: 生成ARB文件时是否抑制元数据,默认为false
  • arbSuppressLastModified: 生成ARB文件时是否抑制最后修改时间,默认为false
  • arbIncludeSourceText: 是否在消息中包含源文本,默认为false

限制

由于这个包基于intl,它继承了一些它的限制,主要的一个是intl使用全局内部映射来在正确的区域设置中查找翻译。

这意味着如果你有多个ARB和自定义的Dart类,你必须确保键在整个应用中是唯一的,否则一个会被另一个覆盖。

另一个限制是Intl无法告诉我们哪些键对应于哪些参数,为了解决这个问题,我们使用源(通常是’en’)ARB文件上的元数据,所以一定要有元数据,否则你会遇到StateError

示例

查看基本的纯Dart示例或我们的Flutter示例

完整示例代码

import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_jimtl/flutter_jimtl.dart';
import 'package:jimtl_codegen_example/translations.dart';

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

class MyApp extends StatefulWidget {
  // 这个widget是你的应用的根
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    //print(TranslationsDelegate.supportedLocales);
    final locales = const [Locale('en'), Locale('fr')];
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      supportedLocales: locales,
      localizationsDelegates: [
        DefaultMaterialLocalizations.delegate,
        TranslationsDelegate<Translations>(
          defaultLocale: locales.first,
          currentFlavor: 'flavor1',
          onTranslationsUpdated: () {
            print('TX updated, need rebuild');
            setState(() {});
          },
          supportedLocales: locales,
          dataLoader: (locale, flavor) async {
            print('local load $locale and $flavor');
            if (flavor == IntlDelegate.defaultFlavorName) {
              return await rootBundle.loadString('assets/arb/translations_$locale.arb');
            }
            return await rootBundle.loadString('assets/arb/translations_${flavor}_$locale.arb');
          },
          updateDataLoader: (locale, flavor) async {
            print('Remote load $locale and $flavor');
            if (locale == 'en' && flavor == 'flavor1') {
              await Future.delayed(Duration(seconds: 10)); // 模拟一些缓慢的网络响应
              return await rootBundle.loadString('assets/arb/translations_remote_$locale.arb');
            }
            return null; // 无更新
          },
          defaultFlavor: IntlDelegate.defaultFlavorName,
          translationsBuilder: () => Translations(),
        ),
      ],
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  Translations getTranslations(BuildContext context) {
    return Translations(); //Translations.of(context);
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(getTranslations(context).counter),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              getTranslations(context).counterPushed(_counter),
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: getTranslations(context).increment,
        child: Icon(Icons.add),
      ), // 这个逗号使自动格式化更美观
    );
  }
}

更多关于Flutter即时通讯插件flutter_jimtl的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter即时通讯插件flutter_jimtl的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用flutter_jimtl插件来实现即时通讯功能的简单示例代码。请注意,flutter_jimtl是一个假设的插件名称,实际使用中请替换为真实存在的Flutter即时通讯插件。由于具体的插件API和实现细节可能会有所不同,以下代码仅供参考,并需要根据实际插件文档进行调整。

首先,确保你已经在pubspec.yaml文件中添加了flutter_jimtl依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_jimtl: ^latest_version  # 请替换为实际版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤实现即时通讯功能:

  1. 初始化插件

在你的主文件(例如main.dart)中,初始化flutter_jimtl插件。

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  // 初始化flutter_jimtl插件
  JimTL.instance.init();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter即时通讯示例',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChatScreen(),
    );
  }
}
  1. 创建聊天屏幕

创建一个新的Dart文件(例如chat_screen.dart),用于构建聊天屏幕。

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

class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _messageController = TextEditingController();
  List<Message> _messages = [];

  @override
  void initState() {
    super.initState();
    // 监听新消息事件
    JimTL.instance.onMessageReceived.listen((Message message) {
      setState(() {
        _messages.insert(0, message); // 新消息插入到列表顶部
      });
    });
  }

  @override
  void dispose() {
    _messageController.dispose();
    super.dispose();
  }

  void _sendMessage() {
    String messageText = _messageController.text.trim();
    if (messageText.isNotEmpty) {
      // 发送消息(假设有一个toUserId表示接收者)
      JimTL.instance.sendMessage(toUserId: 'recipient_user_id', message: messageText);
      _messageController.clear();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('聊天'),
      ),
      body: Column(
        children: <Widget>[
          Expanded(
            child: ListView.builder(
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                Message message = _messages[index];
                return ListTile(
                  title: Text(message.text),
                  subtitle: Text(message.senderId),
                );
              },
            ),
          ),
          Divider(),
          Container(
            decoration: BoxDecoration(color: Colors.grey[200]),
            child: Row(
              children: <Widget>[
                Expanded(
                  child: TextField(
                    controller: _messageController,
                    decoration: InputDecoration(border: InputBorder.none, hintText: '输入消息...'),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.send),
                  onPressed: _sendMessage,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// 假设Message类如下(实际使用时请根据插件文档定义)
class Message {
  String senderId;
  String text;

  Message({required this.senderId, required this.text});
}
  1. 配置插件(如果需要)

根据flutter_jimtl插件的文档,你可能需要在AndroidManifest.xmlInfo.plist中添加必要的配置,以支持即时通讯功能。例如,配置服务器地址、权限等。

请注意,上述代码是一个简化的示例,用于演示如何在Flutter中使用即时通讯插件。实际项目中,你可能需要处理更多的细节,如用户认证、消息状态更新(已发送、已读等)、错误处理等。此外,务必参考flutter_jimtl(或你实际使用的插件)的官方文档,以获取最准确和详细的API信息和使用指南。

回到顶部