Adds real-time connectivity monitoring and visual feedback across the app. Offline icon appears in all app bars when disconnected, cached content shows download badges, and access control prevents browsing servers or opening uncached content without internet connection. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
112 lines
3.2 KiB
Dart
112 lines
3.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:worldhopper/providers/connectivity_provider.dart';
|
|
|
|
/// Custom AppBar that automatically shows offline indicator
|
|
///
|
|
/// This widget wraps the standard AppBar and adds an offline icon
|
|
/// (cloud_off) when the device is not connected to the internet.
|
|
///
|
|
/// Usage: Replace standard AppBar with WorldhopperAppBar
|
|
/// ```dart
|
|
/// WorldhopperAppBar(
|
|
/// title: Text('My Screen'),
|
|
/// actions: [
|
|
/// IconButton(icon: Icon(Icons.search), onPressed: () {}),
|
|
/// ],
|
|
/// )
|
|
/// ```
|
|
class WorldhopperAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|
final Widget? title;
|
|
final List<Widget>? actions;
|
|
final Widget? leading;
|
|
final bool automaticallyImplyLeading;
|
|
final PreferredSizeWidget? bottom;
|
|
final double? elevation;
|
|
final Color? backgroundColor;
|
|
final IconThemeData? iconTheme;
|
|
final bool centerTitle;
|
|
final double? leadingWidth;
|
|
final TextStyle? titleTextStyle;
|
|
final Color? foregroundColor;
|
|
final Color? shadowColor;
|
|
final Color? surfaceTintColor;
|
|
final ShapeBorder? shape;
|
|
final double? scrolledUnderElevation;
|
|
final bool forceMaterialTransparency;
|
|
final Clip? clipBehavior;
|
|
|
|
const WorldhopperAppBar({
|
|
super.key,
|
|
this.title,
|
|
this.actions,
|
|
this.leading,
|
|
this.automaticallyImplyLeading = true,
|
|
this.bottom,
|
|
this.elevation,
|
|
this.backgroundColor,
|
|
this.iconTheme,
|
|
this.centerTitle = false,
|
|
this.leadingWidth,
|
|
this.titleTextStyle,
|
|
this.foregroundColor,
|
|
this.shadowColor,
|
|
this.surfaceTintColor,
|
|
this.shape,
|
|
this.scrolledUnderElevation,
|
|
this.forceMaterialTransparency = false,
|
|
this.clipBehavior,
|
|
});
|
|
|
|
@override
|
|
Size get preferredSize => Size.fromHeight(
|
|
kToolbarHeight + (bottom?.preferredSize.height ?? 0.0),
|
|
);
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
// Watch connectivity state
|
|
final connectivityState = ref.watch(connectivityStateProvider);
|
|
|
|
// Check if offline (default to online while loading)
|
|
final isOffline = connectivityState.whenOrNull(
|
|
data: (isOnline) => !isOnline,
|
|
) ??
|
|
false;
|
|
|
|
// Build actions list with offline icon prepended if needed
|
|
final effectiveActions = <Widget>[
|
|
if (isOffline)
|
|
Padding(
|
|
padding: const EdgeInsets.only(right: 8),
|
|
child: Icon(
|
|
Icons.cloud_off,
|
|
color: Theme.of(context).colorScheme.error,
|
|
semanticLabel: 'Offline',
|
|
),
|
|
),
|
|
...?actions,
|
|
];
|
|
|
|
return AppBar(
|
|
title: title,
|
|
actions: effectiveActions.isEmpty ? null : effectiveActions,
|
|
leading: leading,
|
|
automaticallyImplyLeading: automaticallyImplyLeading,
|
|
bottom: bottom,
|
|
elevation: elevation,
|
|
backgroundColor: backgroundColor,
|
|
iconTheme: iconTheme,
|
|
centerTitle: centerTitle,
|
|
leadingWidth: leadingWidth,
|
|
titleTextStyle: titleTextStyle,
|
|
foregroundColor: foregroundColor,
|
|
shadowColor: shadowColor,
|
|
surfaceTintColor: surfaceTintColor,
|
|
shape: shape,
|
|
scrolledUnderElevation: scrolledUnderElevation,
|
|
forceMaterialTransparency: forceMaterialTransparency,
|
|
clipBehavior: clipBehavior,
|
|
);
|
|
}
|
|
}
|