import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shorebird_code_push/shorebird_code_push.dart'; void main() { runApp(const MyApp()); } class ShorebirdPatch extends StatefulWidget { const ShorebirdPatch({super.key}); @override State createState() => _ShorebirdPatchState(); } enum PatchState { loading, noUpdate, updateReady; } enum MainChannelMethods { restart(methodName: "restart"); final String methodName; const MainChannelMethods({required this.methodName}); } class _ShorebirdPatchState extends State { final shorebird = ShorebirdCodePush(); late Timer timer; PatchState patchState = PatchState.noUpdate; final methodChannel = const MethodChannel("MainChannel"); @override void initState() { timer = Timer.periodic(const Duration(minutes: 1), (timer) async { checkForPatch(timer); }); checkForPatch(timer); super.initState(); } Future checkForPatch(Timer timer) async { print('timer activated'); if (await shorebird.isNewPatchReadyToInstall()) { print('timer await shorebird.isNewPatchReadyToInstall()'); setState(() { patchState = PatchState.updateReady; }); return; } if (!shorebird.isShorebirdAvailable()) { print('timer !shorebird.isShorebirdAvailable()'); timer.cancel(); return; } if (!await shorebird.isNewPatchAvailableForDownload()) { print('timer !await shorebird.isNewPatchAvailableForDownload()'); setState(() { patchState = PatchState.noUpdate; }); return; } final download = shorebird.downloadUpdateIfAvailable(); setState(() { patchState = PatchState.loading; }); await download; if (await shorebird.isNewPatchReadyToInstall()) { print('timer await shorebird.isNewPatchReadyToInstall()'); setState(() { patchState = PatchState.updateReady; }); return; } print('timer end'); } @override void dispose() { timer.cancel(); super.dispose(); } @override Widget build(BuildContext context) { switch (patchState) { case PatchState.loading: return const SizedBox.square(dimension: 48, child: Center(child: CircularProgressIndicator())); case PatchState.noUpdate: return const SizedBox.shrink(); case PatchState.updateReady: return InkWell( child: const SizedBox.square(dimension: 48, child: Icon(Icons.update, size: 24)), onTap: () { methodChannel.invokeMethod(MainChannelMethods.restart.methodName); }, ); } } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: String.fromEnvironment('TITLE')), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), actions: const [ShorebirdPatch()], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'This is a release 1.9.9#6', // 'You have times:', ), const Text( 'You have pushed the button this many times:', // 'You have times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }