Skip to content

Instantly share code, notes, and snippets.

@Sp4Rx
Created July 13, 2024 13:53
Show Gist options
  • Select an option

  • Save Sp4Rx/74d3a9a9dc80c73212cdda5091523fc7 to your computer and use it in GitHub Desktop.

Select an option

Save Sp4Rx/74d3a9a9dc80c73212cdda5091523fc7 to your computer and use it in GitHub Desktop.
Reusable App Bar and Scafold in Flutter (Pseudo code only)
class AppAppBar extends StatelessWidget implements PreferredSizeWidget {
final WidgetBuilder? secondaryAction;
final bool showBackButton;
final String title;
final VoidCallback? onBackPressed;
final String? popScopeDescription;
/// Called when the pop scope is invoked and yes pressed after showYesNoDialog
final VoidCallback? onPopScopeDiscard;
const AppAppBar({
super.key,
this.secondaryAction,
this.showBackButton = true,
this.title = '',
this.onBackPressed,
this.popScopeDescription,
this.onPopScopeDiscard,
});
@override
Widget build(BuildContext context) {
final description = popScopeDescription;
if (description != null && description.isNotEmpty) {
return PopScope(
canPop: false,
onPopInvoked: (didPop) async {
if (didPop) {
return;
}
final result = await showYesNoDialog(
context: context,
title: 'Discard',
subTitle: description,
);
if (result && context.mounted) {
onPopScopeDiscard?.call();
Navigator.pop(context);
}
},
child: _buildWidget(context));
}
return _buildWidget(context);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
Widget _buildWidget(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 16, left: 16, right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Visibility(
visible: showBackButton,
maintainAnimation: title.isEmpty,
maintainSize: title.isEmpty,
maintainState: title.isEmpty,
child: TransParentRoundIconButton(
Icons.arrow_back,
onPressed: () {
final tempOnBackPressed = onBackPressed;
tempOnBackPressed != null ? tempOnBackPressed.call() : Navigator.maybePop(context);
},
),
),
if (title.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 8),
child: Text(title),
),
],
),
if (secondaryAction != null) secondaryAction!(context),
],
),
);
}
}
class AppScaffold extends StatelessWidget {
final Widget? background;
final Widget? body;
final WidgetBuilder? secondaryAction;
final bool showAppBar;
final bool showBackground;
final Widget? bottomNavigationBar;
final bool showBackButton;
final String title;
final VoidCallback? onBackPressed;
final String? popScopeDescription;
final VoidCallback? onPopScopeDiscard;
final bool? extendBody;
const AppScaffold({
super.key,
this.background,
this.body,
this.secondaryAction,
this.showAppBar = true,
this.showBackground = true,
this.showBackButton = true,
this.bottomNavigationBar,
this.title = '',
this.onBackPressed,
this.popScopeDescription,
this.onPopScopeDiscard,
this.extendBody,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: showAppBar
? AppAppBar(
secondaryAction: secondaryAction,
showBackButton: showBackButton,
title: title,
onBackPressed: onBackPressed,
popScopeDescription: popScopeDescription,
onPopScopeDiscard: onPopScopeDiscard,
)
: null,
bottomNavigationBar: bottomNavigationBar,
extendBody: extendBody ?? true,
extendBodyBehindAppBar: true,
backgroundColor: Colors.transparent,
body: Stack(
children: [
if (showBackground) (background != null) ? background! : const TopGradient(),
if (body != null) body!,
],
),
);
}
}
//The `secondaryAction` has a builder pattern so that we can use bloc builder only for this button state updates
AppScaffold(
secondaryAction: (context) => BlocBuilder<SomeBloc, BlocState>(
buildWhen: (previous, current) => current is BlocSuccessState,
builder: (context, state) {
return Visibility(
visible: context.read<SomeBloc>().data.isNotEmpty ?? false,
child: TransParentRoundIconButton(
Icons.check,
onPressed: () => _onConfirm(context),
),
);
}),);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment