feat(legacy): replace CircularProgressIndicator with animated GIF loading

Add LegacyLoadingIndicator widget using a transparent GIF animation
for all full-page loading states across legacy modules. Also fix
HealthController crash by deferring provider mutation during build.
This commit is contained in:
2026-04-23 14:54:38 +02:00
parent ac493725cf
commit 5be9136e06
26 changed files with 60 additions and 30 deletions

View File

@@ -40,7 +40,7 @@ class AppUsersScreen extends ConsumerWidget {
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
itemCount: users.length,
),
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => const SizedBox.shrink(),
),
),

View File

@@ -60,7 +60,7 @@ class LinkedDevicesScreen extends ConsumerWidget {
itemCount: devices.length,
),
),
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => const SizedBox.shrink(),
),
);

View File

@@ -20,7 +20,7 @@ class PersonalDataScreen extends ConsumerWidget {
data: (user) => _PersonalDataForm(user: user),
loading: () => Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
),
error: (_, __) => LegacyPageLayout(
title: context.translate(I18n.personalData),

View File

@@ -40,7 +40,7 @@ class ControlPanelScreen extends ConsumerWidget {
backgroundColor: Theme.of(context).colorScheme.surface,
body: asyncState.when(
skipLoadingOnReload: true,
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (error, _) => RefreshableErrorState(
message: formatErrorMessage(error),
onRefresh: () async {

View File

@@ -10,6 +10,7 @@ import 'package:device_management/src/features/activity_meter/presentation/widge
import 'package:device_management/src/features/activity_meter/presentation/widgets/steps_bar_chart.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/steps_history_section.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/steps_progress_ring.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_ui/legacy_ui.dart';
@@ -27,7 +28,7 @@ class ActivityMeterScreen extends ConsumerWidget {
return LegacyPageLayout(
title: context.translate(I18n.activityMeter),
body: isLoading
? const Center(child: CircularProgressIndicator())
? const LegacyLoadingIndicator()
: const _ActivityMeterBody(),
);
}

View File

@@ -2,6 +2,7 @@ import 'package:device_management/src/core/presentation/widgets/time_range_selec
import 'package:device_management/src/features/apps_use/presentation/providers/apps_use_controller.dart';
import 'package:device_management/src/features/apps_use/presentation/widgets/daily_app_usage_section.dart';
import 'package:device_management/src/features/apps_use/presentation/widgets/top_apps_section.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_ui/legacy_ui.dart';
@@ -29,7 +30,7 @@ class AppsUseScreen extends ConsumerWidget {
return LegacyPageLayout(
title: context.translate(I18n.appsUse),
body: state.isLoading
? const Center(child: CircularProgressIndicator())
? const LegacyLoadingIndicator()
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,

View File

@@ -105,13 +105,13 @@ class BackgroundImageScreen extends ConsumerWidget {
body: SafeArea(
top: false,
child: photosAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.errorBackgroundImageLoad)),
),
data: (photos) {
if (isSaving) {
return const Center(child: CircularProgressIndicator());
return const LegacyLoadingIndicator();
}
if (photos.isEmpty) {
return _EmptyState(

View File

@@ -1,6 +1,7 @@
import 'package:device_management/src/features/call_history/data/call_history_entity.dart';
import 'package:device_management/src/features/call_history/presentation/providers/call_history_filter_provider.dart';
import 'package:device_management/src/features/call_history/presentation/providers/call_history_provider.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_theme/legacy_theme.dart';
@@ -28,7 +29,7 @@ class CallHistoryScreen extends ConsumerWidget {
return LegacyPageLayout(
title: context.translate(I18n.callHistory),
body: callsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (err, _) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,

View File

@@ -57,14 +57,14 @@ class ContactsScreen extends ConsumerWidget {
showEdit: true,
onEditChange: ref.read(contactsEditingModeProvider.notifier).toggle,
body: userAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.errorGeneric)),
),
data: (user) {
final contactsAsync = ref.watch(contactsProvider(user.id));
return contactsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.errorGeneric)),
),

View File

@@ -26,7 +26,7 @@ class EditContactScreen extends ConsumerWidget {
backgroundColor: Theme.of(context).colorScheme.surface,
body: SafeArea(
child: userAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.errorGeneric)),
),
@@ -34,7 +34,7 @@ class EditContactScreen extends ConsumerWidget {
final contactsAsync = ref.watch(contactsProvider(user.id));
return contactsAsync.when(
loading: () =>
const Center(child: CircularProgressIndicator()),
const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.errorGeneric)),
),

View File

@@ -35,7 +35,7 @@ class DoNotDisturbScreen extends ConsumerWidget {
if (device == null) {
return LegacyPageLayout(
title: context.translate(I18n.doNotDisturb),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
);
}
@@ -50,7 +50,7 @@ class DoNotDisturbScreen extends ConsumerWidget {
return LegacyPageLayout(
title: context.translate(I18n.doNotDisturb),
body: periodsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.doNotDisturbError)),
),

View File

@@ -80,7 +80,7 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
return LegacyPageLayout(
title: context.translate(I18n.healthTitle),
body: state.isLoading
? const Center(child: CircularProgressIndicator())
? const LegacyLoadingIndicator()
: state.isMeasuringCountdown
? _MeasuringOverlay(
remainingSeconds: state.measureRemainingSeconds,

View File

@@ -36,7 +36,9 @@ class HealthController extends _$HealthController {
final remaining = endTime.difference(DateTime.now()).inSeconds;
if (remaining <= 0) {
ref.read(measureEndTimeProvider.notifier).set(null);
Future.microtask(() {
ref.read(measureEndTimeProvider.notifier).set(null);
});
return;
}

View File

@@ -27,7 +27,7 @@ class RemoteCameraScreen extends ConsumerWidget {
if (device == null) {
return LegacyPageLayout(
title: context.translate(I18n.remoteCamera),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
);
}
@@ -37,14 +37,14 @@ class RemoteCameraScreen extends ConsumerWidget {
Widget body;
if (cameraState.isTakingPicture) {
body = const Center(child: CircularProgressIndicator());
body = const LegacyLoadingIndicator();
} else if (cameraState.isWaitingForPhoto) {
body = _WaitingForPhotoOverlay(
remainingSeconds: cameraState.photoCountdown,
);
} else {
body = picturesAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) =>
Center(child: Text(context.translate(I18n.errorFetchPhotos))),
data: (pictures) => _GallerySection(pictures: pictures),

View File

@@ -2,6 +2,7 @@ import 'package:device_management/src/features/scheduled_activities/presentation
import 'package:device_management/src/features/scheduled_activities/presentation/providers/scheduled_activities_provider.dart';
import 'package:device_management/src/features/scheduled_activities/presentation/widgets/activity_form_sheet.dart';
import 'package:device_management/src/features/scheduled_activities/presentation/widgets/day_timeline.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
@@ -82,7 +83,7 @@ class _ScheduledActivitiesScreenState
if (device == null) {
return LegacyPageLayout(
title: context.translate(I18n.activityScheduleTitle),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
);
}
@@ -92,7 +93,7 @@ class _ScheduledActivitiesScreenState
return LegacyPageLayout(
title: context.translate(I18n.activityScheduleTitle),
body: activitiesAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (err, _) => Center(child: Text(err.toString())),
data: (activities) => Column(
children: [

View File

@@ -31,7 +31,7 @@ class VolumeControlScreen extends ConsumerWidget {
if (device == null) {
return LegacyPageLayout(
title: context.translate(I18n.volumeControl),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
);
}

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
@@ -48,7 +49,7 @@ class LocationScreen extends ConsumerWidget {
title: context.translate(I18n.mapTitle),
showBack: false,
body: isLoading
? const Center(child: CircularProgressIndicator())
? const LegacyLoadingIndicator()
: deviceState == null
? RefreshableErrorState(
onRefresh: () async {

View File

@@ -50,7 +50,7 @@ class AlarmScreen extends ConsumerWidget {
return alarmsAsync.when(
loading: () => Scaffold(
appBar: _appBar(context, navigationContract, primaryColor),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
),
error: (_, __) => Scaffold(
appBar: _appBar(context, navigationContract, primaryColor),

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
@@ -46,7 +47,7 @@ class BlockPhoneScreen extends ConsumerWidget {
return contactsAsync.when(
loading: () => Scaffold(
appBar: _appBar(context, navigationContract, primaryColor),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
),
error: (_, __) => Scaffold(
appBar: _appBar(context, navigationContract, primaryColor),

View File

@@ -40,7 +40,7 @@ class DisableFunctionsScreen extends ConsumerWidget {
return LegacyPageLayout(
title: context.translate(I18n.disableFunctions),
body: device == null
? const Center(child: CircularProgressIndicator())
? const LegacyLoadingIndicator()
: Padding(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 16, big: 14),

View File

@@ -2,6 +2,7 @@ import 'package:settings/src/core/domain/entities/notification_entity.dart';
import 'package:settings/src/features/notifications/presentation/providers/notifications_feed_provider.dart';
import 'package:settings/src/features/notifications/presentation/providers/notifications_filter_provider.dart';
import 'package:settings/src/features/notifications/presentation/widgets/notification_card.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_theme/legacy_theme.dart';
@@ -216,7 +217,7 @@ class _FilteredNotificationsScreen extends ConsumerWidget {
),
),
body: feedAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.alertsLoadError)),
),

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_theme/legacy_theme.dart';
@@ -48,7 +49,7 @@ class SosContactsScreen extends ConsumerWidget {
return contactsAsync.when(
loading: () => Scaffold(
appBar: _appBar(context, navigationContract, primaryColor),
body: const Center(child: CircularProgressIndicator()),
body: const LegacyLoadingIndicator(),
),
error: (_, __) => Scaffold(
appBar: _appBar(context, navigationContract, primaryColor),

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
@@ -97,7 +98,7 @@ class _WifiSettingsScreenState extends ConsumerState<WifiSettingsScreen> {
body: SafeArea(
top: false,
child: savedAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const LegacyLoadingIndicator(),
error: (_, __) => Center(
child: Text(context.translate(I18n.wifiLoadError)),
),

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

View File

@@ -15,5 +15,6 @@ export 'src/containers/section_container.dart';
export 'src/containers/footer_container.dart';
export 'src/rows/editable_row.dart';
export 'src/loading/app_loading_indicator.dart';
export 'src/loading/legacy_loading_indicator.dart';
export 'src/confetti/confetti_overlay.dart';
export 'src/dialogs/contacts_permission_dialog.dart';

View File

@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class LegacyLoadingIndicator extends StatelessWidget {
final double size;
const LegacyLoadingIndicator({super.key, this.size = 120});
@override
Widget build(BuildContext context) {
return Center(
child: Image.asset(
'packages/design_system/assets/animations/loading_legacy.gif',
width: size,
height: size,
),
);
}
}