Flutter桌面小组件插件home_widget的使用

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

Flutter桌面小组件插件home_widget的使用

Home Widget 插件简介

HomeWidget 是一个Flutter插件,旨在简化在Android和iOS上创建主屏幕小部件的过程。该插件提供了统一的接口来发送数据、检索数据以及更新小部件,但请注意,它并不允许直接用Flutter编写小部件,仍需使用原生代码进行小部件开发。

功能特性

  • 设置小部件:帮助在原生侧设置小部件。
  • 发送与更新:从Flutter向主屏幕小部件发送数据并更新它们。
  • 交互式小部件:支持响应用户交互的小部件,可调用Dart代码。

使用指南

保存数据到小部件

要将数据保存到小部件中,请使用saveWidgetData方法:

HomeWidget.saveWidgetData<String>('id', data);

更新小部件

为了触发主屏幕小部件的重新加载,需要调用以下方法:

HomeWidget.updateWidget(
    name: 'HomeWidgetExampleProvider',
);

对于Android平台,还需要指定具体的接收者名称:

HomeWidget.updateWidget(
    qualifiedAndroidName: 'es.antonborri.home_widget_example.glance.HomeWidgetReceiver',
);

示例Demo

下面是一个完整的示例应用程序,演示了如何使用home_widget插件创建和管理主屏幕小部件。此示例展示了如何发送和更新小部件的数据,并处理来自小部件的互动事件。

import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:home_widget/home_widget.dart';
import 'package:workmanager/workmanager.dart';

/// 背景任务调度器
@pragma("vm:entry-point")
void callbackDispatcher() async {
  Workmanager().executeTask((taskName, inputData) {
    final now = DateTime.now();
    return Future.wait<bool?>([
      HomeWidget.saveWidgetData('title', 'Updated from Background'),
      HomeWidget.saveWidgetData('message', '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}')
    ]).then((value) async {
      await Future.wait([
        HomeWidget.updateWidget(name: 'HomeWidgetExampleProvider', iOSName: 'HomeWidgetExample'),
        if (Platform.isAndroid)
          HomeWidget.updateWidget(qualifiedAndroidName: 'es.antonborri.home_widget_example.glance.HomeWidgetReceiver')
      ]);
      return !value.contains(false);
    });
  });
}

/// 小部件交互回调
@pragma("vm:entry-point")
Future<void> interactiveCallback(Uri? data) async {
  if (data?.host == 'titleclicked') {
    final greetings = ['Hello', 'Hallo', 'Bonjour', 'Hola', 'Ciao', '哈洛', '안녕하세요', 'xin chào'];
    final selectedGreeting = greetings[Random().nextInt(greetings.length)];
    await HomeWidget.setAppGroupId('YOUR_GROUP_ID');
    await HomeWidget.saveWidgetData<String>('title', selectedGreeting);
    await HomeWidget.updateWidget(name: 'HomeWidgetExampleProvider', iOSName: 'HomeWidgetExample');
    if (Platform.isAndroid) {
      await HomeWidget.updateWidget(qualifiedAndroidName: 'es.antonborri.home_widget_example.glance.HomeWidgetReceiver');
    }
  }
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Workmanager().initialize(callbackDispatcher, isInDebugMode: kDebugMode);
  runApp(const MaterialApp(home: MyApp()));
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final TextEditingController _titleController = TextEditingController();
  final TextEditingController _messageController = TextEditingController();

  bool _isRequestPinWidgetSupported = false;

  @override
  void initState() {
    super.initState();
    HomeWidget.setAppGroupId('YOUR_GROUP_ID');
    HomeWidget.registerInteractivityCallback(interactiveCallback);
    _checkPinability();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _checkForWidgetLaunch();
    HomeWidget.widgetClicked.listen(_launchedFromWidget);
  }

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

  Future _sendData() async {
    try {
      return Future.wait([
        HomeWidget.saveWidgetData<String>('title', _titleController.text),
        HomeWidget.saveWidgetData<String>('message', _messageController.text),
        HomeWidget.renderFlutterWidget(const Icon(Icons.flutter_dash, size: 200), logicalSize: const Size(200, 200), key: 'dashIcon')
      ]);
    } on PlatformException catch (exception) {
      debugPrint('Error Sending Data. $exception');
    }
  }

  Future _updateWidget() async {
    try {
      return Future.wait([
        HomeWidget.updateWidget(name: 'HomeWidgetExampleProvider', iOSName: 'HomeWidgetExample'),
        if (Platform.isAndroid)
          HomeWidget.updateWidget(qualifiedAndroidName: 'es.antonborri.home_widget_example.glance.HomeWidgetReceiver')
      ]);
    } on PlatformException catch (exception) {
      debugPrint('Error Updating Widget. $exception');
    }
  }

  Future _loadData() async {
    try {
      return Future.wait([
        HomeWidget.getWidgetData<String>('title', defaultValue: 'Default Title').then((value) => _titleController.text = value ?? ''),
        HomeWidget.getWidgetData<String>('message', defaultValue: 'Default Message').then((value) => _messageController.text = value ?? '')
      ]);
    } on PlatformException catch (exception) {
      debugPrint('Error Getting Data. $exception');
    }
  }

  Future<void> _sendAndUpdate() async {
    await _sendData();
    await _updateWidget();
  }

  void _checkForWidgetLaunch() {
    HomeWidget.initiallyLaunchedFromHomeWidget().then(_launchedFromWidget);
  }

  void _launchedFromWidget(Uri? uri) {
    if (uri != null) {
      showDialog(
        context: context,
        builder: (buildContext) => AlertDialog(
          title: const Text('App started from HomeScreenWidget'),
          content: Text('Here is the URI: $uri'),
        ),
      );
    }
  }

  void _startBackgroundUpdate() {
    Workmanager().registerPeriodicTask('1', 'widgetBackgroundUpdate', frequency: const Duration(minutes: 15));
  }

  void _stopBackgroundUpdate() {
    Workmanager().cancelByUniqueName('1');
  }

  Future<void> _getInstalledWidgets() async {
    try {
      final widgets = await HomeWidget.getInstalledWidgets();
      if (!mounted) return;

      String getText(HomeWidgetInfo widget) {
        if (Platform.isIOS) {
          return 'iOS Family: ${widget.iOSFamily}, iOS Kind: ${widget.iOSKind}';
        } else {
          return 'Android Widget id: ${widget.androidWidgetId}, Android Class Name: ${widget.androidClassName}, Android Label: ${widget.androidLabel}';
        }
      }

      await showDialog(
        context: context,
        builder: (buildContext) => AlertDialog(
          title: const Text('Installed Widgets'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('Number of widgets: ${widgets.length}'),
              const Divider(),
              for (final widget in widgets)
                Text(getText(widget)),
            ],
          ),
        ),
      );
    } on PlatformException catch (exception) {
      debugPrint('Error getting widget information. $exception');
    }
  }

  Future<void> _checkPinability() async {
    final isRequestPinWidgetSupported = await HomeWidget.isRequestPinWidgetSupported();
    if (mounted) {
      setState(() {
        _isRequestPinWidgetSupported = isRequestPinWidgetSupported ?? false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('HomeWidget Example'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              TextField(
                decoration: const InputDecoration(hintText: 'Title'),
                controller: _titleController,
              ),
              TextField(
                decoration: const InputDecoration(hintText: 'Body'),
                controller: _messageController,
              ),
              ElevatedButton(onPressed: _sendAndUpdate, child: const Text('Send Data to Widget')),
              ElevatedButton(onPressed: _loadData, child: const Text('Load Data')),
              ElevatedButton(onPressed: _checkForWidgetLaunch, child: const Text('Check For Widget Launch')),
              if (Platform.isAndroid)
                ElevatedButton(onPressed: _startBackgroundUpdate, child: const Text('Update in background')),
              if (Platform.isAndroid)
                ElevatedButton(onPressed: _stopBackgroundUpdate, child: const Text('Stop updating in background')),
              ElevatedButton(onPressed: _getInstalledWidgets, child: const Text('Get Installed Widgets')),
              if (_isRequestPinWidgetSupported)
                ElevatedButton(
                  onPressed: () => HomeWidget.requestPinWidget(qualifiedAndroidName: 'es.antonborri.home_widget_example.glance.HomeWidgetReceiver'),
                  child: const Text('Pin Widget'),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

本示例代码展示了如何集成home_widget插件以实现基本的小部件功能,包括但不限于:

  • 发送文本数据到小部件。
  • 定期更新小部件内容(仅限Android)。
  • 处理来自小部件的点击事件。
  • 获取当前安装的小部件列表。
  • 请求固定新的小部件(如果平台支持)。

更多关于Flutter桌面小组件插件home_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter桌面小组件插件home_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter桌面应用中集成和使用home_widget插件的代码示例。home_widget插件允许你创建桌面小组件(Widgets)以展示信息,类似于iOS的Today Widgets或Android的小部件。

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

dependencies:
  flutter:
    sdk: flutter
  home_widget: ^最新版本号  # 请替换为实际的最新版本号

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

1. 配置插件

android/app/src/main/AndroidManifest.xml中,添加必要的权限和配置,以允许应用创建桌面小组件。通常,home_widget插件的文档会提供具体的配置要求。这里假设你已经按照文档完成了这些配置。

2. 创建小组件内容

创建一个新的Dart文件,比如my_widget.dart,用于定义小组件的内容。

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

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      padding: EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Text(
            'Hello, World!',
            style: TextStyle(fontSize: 24),
          ),
          SizedBox(height: 16),
          Text(
            'This is a Flutter home widget!',
            style: TextStyle(fontSize: 16),
          ),
        ],
      ),
    );
  }
}

3. 注册小组件

在你的主应用入口文件(通常是main.dart)中,注册小组件。

import 'package:flutter/material.dart';
import 'package:home_widget/home_widget_plugin.dart';
import 'my_widget.dart';

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

  // 注册小组件
  HomeWidgetPlugin.registerWidget(
    identifier: 'my_widget_identifier',
    name: 'My Flutter Widget',
    description: 'A simple Flutter home widget example.',
    builder: (context) => MyWidget(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Home Widget Demo'),
      ),
      body: Center(
        child: Text('Open your home screen to see the widget.'),
      ),
    );
  }
}

4. 运行应用并添加小组件

  1. 运行你的Flutter应用。
  2. 根据操作系统的不同,将小组件添加到主屏幕:
    • macOS: 在通知中心的小组件部分添加。
    • Windows: 通常需要在桌面设置或类似的地方手动添加。
    • Linux: 这取决于桌面环境,可能需要在小组件或桌面设置中添加。

请注意,由于桌面环境的多样性和home_widget插件的具体实现,上述步骤可能需要根据实际使用的操作系统和桌面环境进行调整。此外,home_widget插件的功能和API可能会随着版本的更新而变化,因此建议查阅最新的官方文档以获取最准确的信息。

回到顶部