Flutter代码推送插件shorebird_code_push的使用

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

Flutter代码推送插件shorebird_code_push的使用

简介

Shorebird Code Push 是一个用于Flutter应用的代码热更新解决方案,它允许开发者在不发布新版本的情况下对已部署的应用进行代码更新。通过shorebird_code_push插件,开发者可以在应用中实现以下功能:

  • 获取当前安装的补丁版本
  • 检查是否有新的补丁可用
  • 下载并应用新的补丁

开始使用

如果你的Flutter应用还没有集成Shorebird,首先需要按照官方文档中的指南来添加代码推送功能到你的应用。

安装

在项目的根目录下运行以下命令来添加shorebird_code_push依赖:

flutter pub add shorebird_code_push

使用示例

下面是一个完整的Flutter应用程序示例,演示了如何使用shorebird_code_push插件进行代码更新检查和应用。

示例代码

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

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Shorebird Code Push Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _updater = ShorebirdUpdater();
  late final bool _isUpdaterAvailable;
  var _currentTrack = UpdateTrack.stable;
  var _isCheckingForUpdates = false;
  Patch? _currentPatch;

  @override
  void initState() {
    super.initState();
    // Check whether Shorebird is available.
    setState(() => _isUpdaterAvailable = _updater.isAvailable);

    // Read the current patch (if there is one.)
    // `currentPatch` will be `null` if no patch is installed.
    _updater.readCurrentPatch().then((currentPatch) {
      setState(() => _currentPatch = currentPatch);
    }).catchError((Object error) {
      // If an error occurs, we log it for now.
      debugPrint('Error reading current patch: $error');
    });
  }

  Future<void> _checkForUpdate() async {
    if (_isCheckingForUpdates) return;

    try {
      setState(() => _isCheckingForUpdates = true);
      // Check if there's an update available.
      final status = await _updater.checkForUpdate(track: _currentTrack);
      if (!mounted) return;
      // If there is an update available, show a banner.
      switch (status) {
        case UpdateStatus.upToDate:
          _showNoUpdateAvailableBanner();
        case UpdateStatus.outdated:
          _showUpdateAvailableBanner();
        case UpdateStatus.restartRequired:
          _showRestartBanner();
        case UpdateStatus.unavailable:
        // Do nothing, there is already a warning displayed at the top of the
        // screen.
      }
    } catch (error) {
      // If an error occurs, we log it for now.
      debugPrint('Error checking for update: $error');
    } finally {
      setState(() => _isCheckingForUpdates = false);
    }
  }

  void _showDownloadingBanner() {
    ScaffoldMessenger.of(context)
      ..hideCurrentMaterialBanner()
      ..showMaterialBanner(
        const MaterialBanner(
          content: Text('Downloading...'),
          actions: [
            SizedBox(
              height: 14,
              width: 14,
              child: CircularProgressIndicator(),
            ),
          ],
        ),
      );
  }

  void _showUpdateAvailableBanner() {
    ScaffoldMessenger.of(context)
      ..hideCurrentMaterialBanner()
      ..showMaterialBanner(
        MaterialBanner(
          content: Text(
            'Update available for the ${_currentTrack.name} track.',
          ),
          actions: [
            TextButton(
              onPressed: () async {
                ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
                await _downloadUpdate();
                if (!mounted) return;
                ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
              },
              child: const Text('Download'),
            ),
          ],
        ),
      );
  }

  void _showNoUpdateAvailableBanner() {
    ScaffoldMessenger.of(context)
      ..hideCurrentMaterialBanner()
      ..showMaterialBanner(
        MaterialBanner(
          content: Text(
            'No update available on the ${_currentTrack.name} track.',
          ),
          actions: [
            TextButton(
              onPressed: () {
                ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
              },
              child: const Text('Dismiss'),
            ),
          ],
        ),
      );
  }

  void _showRestartBanner() {
    ScaffoldMessenger.of(context)
      ..hideCurrentMaterialBanner()
      ..showMaterialBanner(
        MaterialBanner(
          content: const Text('A new patch is ready! Please restart your app.'),
          actions: [
            TextButton(
              onPressed: () {
                ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
              },
              child: const Text('Dismiss'),
            ),
          ],
        ),
      );
  }

  void _showErrorBanner(Object error) {
    ScaffoldMessenger.of(context)
      ..hideCurrentMaterialBanner()
      ..showMaterialBanner(
        MaterialBanner(
          content: Text(
            'An error occurred while downloading the update: $error.',
          ),
          actions: [
            TextButton(
              onPressed: () {
                ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
              },
              child: const Text('Dismiss'),
            ),
          ],
        ),
      );
  }

  Future<void> _downloadUpdate() async {
    _showDownloadingBanner();
    try {
      // Perform the update (e.g download the latest patch on [_currentTrack]).
      // Note that [track] is optional. Not passing it will default to the
      // stable track.
      await _updater.update(track: _currentTrack);
      if (!mounted) return;
      // Show a banner to inform the user that the update is ready and that they
      // need to restart the app.
      _showRestartBanner();
    } on UpdateException catch (error) {
      // If an error occurs, we show a banner with the error message.
      _showErrorBanner(error.message);
    }
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: theme.colorScheme.inversePrimary,
        title: const Text('Shorebird Code Push'),
      ),
      body: Column(
        children: [
          if (!_isUpdaterAvailable) const _ShorebirdUnavailable(),
          const Spacer(),
          _CurrentPatchVersion(patch: _currentPatch),
          const SizedBox(height: 12),
          _TrackPicker(
            currentTrack: _currentTrack,
            onChanged: (track) {
              setState(() => _currentTrack = track);
            },
          ),
          const Spacer(),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _isCheckingForUpdates ? null : _checkForUpdate,
        tooltip: 'Check for update',
        child: _isCheckingForUpdates
            ? const _LoadingIndicator()
            : const Icon(Icons.refresh),
      ),
    );
  }
}

/// Widget that is mounted when Shorebird is not available.
class _ShorebirdUnavailable extends StatelessWidget {
  const _ShorebirdUnavailable();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Center(
      child: Text(
        '''
Shorebird is not available.
Please make sure the app was generated via `shorebird release` and that it is running in release mode.''',
        style: theme.textTheme.bodyLarge?.copyWith(
          color: theme.colorScheme.error,
        ),
      ),
    );
  }
}

/// Widget that displays the current patch version.
class _CurrentPatchVersion extends StatelessWidget {
  const _CurrentPatchVersion({required this.patch});

  final Patch? patch;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('Current patch version:'),
          Text(
            patch != null ? '${patch!.number}' : 'No patch installed',
            style: theme.textTheme.headlineMedium,
          ),
        ],
      ),
    );
  }
}

/// Widget that allows selection of update track.
class _TrackPicker extends StatelessWidget {
  const _TrackPicker({
    required this.currentTrack,
    required this.onChanged,
  });

  final UpdateTrack currentTrack;

  final ValueChanged<UpdateTrack> onChanged;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Update track:'),
        SegmentedButton<UpdateTrack>(
          segments: const [
            ButtonSegment(
              label: Text('Stable'),
              value: UpdateTrack.stable,
            ),
            ButtonSegment(
              label: Text('Beta'),
              icon: Icon(Icons.science),
              value: UpdateTrack.beta,
            ),
            ButtonSegment(
              label: Text('Staging'),
              icon: Icon(Icons.construction),
              value: UpdateTrack.staging,
            ),
          ],
          selected: {currentTrack},
          onSelectionChanged: (tracks) => onChanged(tracks.single),
        ),
      ],
    );
  }
}

/// A reusable loading indicator.
class _LoadingIndicator extends StatelessWidget {
  const _LoadingIndicator();

  @override
  Widget build(BuildContext context) {
    return const SizedBox(
      height: 14,
      width: 14,
      child: CircularProgressIndicator(strokeWidth: 2),
    );
  }
}

加入我们

我们有一个活跃的Discord服务器,在那里你可以提问和获得帮助。

贡献

如果你有兴趣为shorebird_code_push贡献代码,请参考CONTRIBUTING.md了解更多信息。

希望这个示例能够帮助你更好地理解和使用shorebird_code_push插件。如果有任何问题,欢迎随时提问!


更多关于Flutter代码推送插件shorebird_code_push的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter代码推送插件shorebird_code_push的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用shorebird_code_push插件进行代码推送的示例代码。

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

dependencies:
  flutter:
    sdk: flutter
  shorebird_code_push: ^latest_version_here  # 替换为最新版本号

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

接下来,配置你的Flutter应用以使用shorebird_code_push。以下是一个基本的实现步骤:

  1. 初始化插件

在你的main.dart文件中,初始化shorebird_code_push插件。

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化代码推送插件
  await ShorebirdCodePush.initialize(
    serverUrl: 'https://your-code-push-server-url.com', // 替换为你的代码推送服务器URL
    deploymentKey: 'your-deployment-key', // 替换为你的部署密钥
    updateDialog: true, // 是否显示更新对话框
    mandatoryUpdate: false, // 是否为强制更新
  );

  // 检查是否有可用的更新
  bool hasUpdate = await ShorebirdCodePush.checkForUpdate();
  if (hasUpdate) {
    // 下载并应用更新
    await ShorebirdCodePush.sync();
  }

  runApp(MyApp());
}

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

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Code Push Demo'),
      ),
      body: Center(
        child: Text('Hello, Code Push!'),
      ),
    );
  }
}
  1. 处理更新回调(可选):

你可以监听更新事件,例如下载进度、更新成功或失败等。

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化代码推送插件
  ShorebirdCodePush.initialize(
    serverUrl: 'https://your-code-push-server-url.com',
    deploymentKey: 'your-deployment-key',
    updateDialog: true,
    mandatoryUpdate: false,
  ).then((_) {
    // 监听下载进度
    ShorebirdCodePush.downloadProgress.listen((progress) {
      print('Download progress: ${progress.receivedBytes}/${progress.totalBytes}');
    });

    // 监听更新状态
    ShorebirdCodePush.updateStatus.listen((status) {
      if (status == UpdateStatus.downloaded) {
        print('Update downloaded, applying...');
      } else if (status == UpdateStatus.applied) {
        print('Update applied successfully!');
        // 这里可以重启应用以应用更新
        // SystemChannels.platform.invokeMethod('SystemNavigator.pop'); // 退出当前应用(iOS)
        // exit(0); // 退出应用(Android)
      } else if (status == UpdateStatus.failed) {
        print('Update failed!');
      }
    });
  });

  // 检查是否有可用的更新
  bool hasUpdate = await ShorebirdCodePush.checkForUpdate();
  if (hasUpdate) {
    // 下载并应用更新
    await ShorebirdCodePush.sync();
  }

  runApp(MyApp());
}

请注意,serverUrldeploymentKey需要替换为你自己的代码推送服务器URL和部署密钥。另外,根据实际需要,你可能需要调整更新对话框的显示逻辑和强制更新的策略。

这个示例展示了如何初始化shorebird_code_push插件、检查更新、下载更新以及监听更新事件。确保你的服务器配置正确,并且已经上传了相应的代码包。

回到顶部