import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:pub_semver/pub_semver.dart'; import '../../api/client.dart'; import '../../api/endpoints/health.dart'; import '../../auth/auth_provider.dart'; final _versionCheckProvider = FutureProvider<_VersionResult>((ref) async { final url = await ref.watch(serverUrlProvider.future); if (url == null) return _VersionResult.skipped; final dio = ApiClient.buildDio(baseUrl: url, tokenResolver: () async => null); final body = await HealthApi(dio).check(); final min = body['min_client_version']; if (min == null || min.isEmpty) return _VersionResult.skipped; final info = await PackageInfo.fromPlatform(); final mine = Version.parse(info.version); final required = Version.parse(min); return mine < required ? _VersionResult.tooOld : _VersionResult.ok; }); enum _VersionResult { ok, tooOld, skipped } class VersionGate extends ConsumerWidget { const VersionGate({required this.child, super.key}); final Widget child; @override Widget build(BuildContext context, WidgetRef ref) { return ref.watch(_versionCheckProvider).when( data: (r) => r == _VersionResult.tooOld ? const _TooOldScreen() : child, error: (_, __) => child, // soft-fail if /healthz is unreachable; let the rest of the flow surface a real error loading: () => const Scaffold(body: Center(child: CircularProgressIndicator())), ); } } class _TooOldScreen extends StatelessWidget { const _TooOldScreen(); @override Widget build(BuildContext context) { return const Scaffold( body: SafeArea( child: Padding( padding: EdgeInsets.all(24), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Update required', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w500), ), SizedBox(height: 12), Text( 'This client is too old for that Minstrel server. Install the latest build to continue.', textAlign: TextAlign.center, ), ], ), ), ), ), ); } }