Skip to content

Commit

Permalink
#336: added base screen
Browse files Browse the repository at this point in the history
  • Loading branch information
jorre127 committed Oct 10, 2024
1 parent 4770987 commit 47f8ca2
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 25 deletions.
43 changes: 18 additions & 25 deletions lib/screen/todo/todo_list/todo_list_screen.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_template/di/injectable.dart';
import 'package:flutter_template/model/webservice/todo/todo.dart';
import 'package:flutter_template/styles/theme_assets.dart';
import 'package:flutter_template/styles/theme_dimens.dart';
import 'package:flutter_template/util/keys.dart';
import 'package:flutter_template/viewmodel/todo/todo_list/todo_list_viewmodel.dart';
import 'package:flutter_template/widget/general/action/action_item.dart';
import 'package:flutter_template/widget/general/simple_screen/simple_screen.dart';
import 'package:flutter_template/widget/general/styled/flutter_template_progress_indicator.dart';
import 'package:flutter_template/widget/provider/provider_widget.dart';
import 'package:flutter_template/widget/todo/todo_row_item.dart';
import 'package:icapps_architecture/icapps_architecture.dart';

class TodoListScreen extends StatefulWidget {
const TodoListScreen({super.key});
Expand All @@ -27,29 +26,23 @@ class TodoListScreenState extends State<TodoListScreen> {
create: () => getIt()..init(),
consumerWithThemeAndLocalization: (context, viewModel, child, theme, localization) {
final errorKey = viewModel.errorKey;
return Scaffold(
backgroundColor: theme.colorsTheme.background,
appBar: AppBar(
title: Text(localization.todoTitle),
systemOverlayStyle: SystemUiOverlayStyle.light,
centerTitle: context.isIOSTheme,
backgroundColor: theme.colorsTheme.primary,
actions: [
ActionItem(
key: Keys.downloadAction,
svgAsset: ThemeAssets.downloadIcon(context),
onClick: viewModel.onDownloadClicked,
color: theme.colorsTheme.appBarAction,
),
ActionItem(
key: Keys.addAction,
svgAsset: ThemeAssets.addIcon(context),
onClick: viewModel.onAddClicked,
color: theme.colorsTheme.appBarAction,
),
],
),
body: Builder(
return BaseScreen.child(
title: localization.todoTitle,
actions: [
ActionItem(
key: Keys.downloadAction,
svgAsset: ThemeAssets.downloadIcon(context),
onClick: viewModel.onDownloadClicked,
color: theme.colorsTheme.appBarAction,
),
ActionItem(
key: Keys.addAction,
svgAsset: ThemeAssets.addIcon(context),
onClick: viewModel.onAddClicked,
color: theme.colorsTheme.appBarAction,
),
],
child: Builder(
builder: (context) {
if (viewModel.isLoading) return Center(child: FlutterTemplateProgressIndicator(dark: theme.isLightTheme));
if (errorKey != null) {
Expand Down
153 changes: 153 additions & 0 deletions lib/widget/general/simple_screen/base_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_template/widget/general/simple_screen/base_screen_content.dart';
import 'package:flutter_template/widget/general/simple_screen/base_screen_header.dart';
import 'package:flutter_template/widget/general/status_bar.dart';
import 'package:flutter_template/widget/general/styled/flutter_template_progress_indicator.dart';
import 'package:flutter_template/widget/provider/data_provider_widget.dart';

class BaseScreen extends StatelessWidget {
final bool isScrollable;
final bool useSlivers;
final bool showHeader;
final bool isLoading;
final bool hasBottomSafeSpace;
final int? itemCount;
final Color? background;
final String? title;
final Widget? child;
final EdgeInsets padding;
final List<Widget> children;
final List<Widget>? actions;
final VoidCallback? onBackTapped;
final AsyncCallback? onRefresh;
final IndexedWidgetBuilder? itemBuilder;

const BaseScreen({
required this.children,
this.actions,
this.onRefresh,
this.isLoading = false,
this.hasBottomSafeSpace = true,
this.onBackTapped,
this.title,
this.showHeader = true,
this.isScrollable = false,
this.useSlivers = false,
this.padding = const EdgeInsets.all(16),
this.background,
super.key,
}) : child = null,
itemBuilder = null,
itemCount = null;

const BaseScreen.builder({
required this.itemBuilder,
required this.itemCount,
this.actions,
this.onRefresh,
this.isLoading = false,
this.hasBottomSafeSpace = true,
this.onBackTapped,
this.title,
this.showHeader = true,
this.padding = const EdgeInsets.all(16),
this.background,
super.key,
}) : children = const [],
child = null,
useSlivers = false,
isScrollable = true;

const BaseScreen.slivers({
required this.children,
this.actions,
this.onRefresh,
this.hasBottomSafeSpace = true,
this.onBackTapped,
this.isLoading = false,
this.title,
this.showHeader = true,
this.padding = const EdgeInsets.all(16),
this.background,
super.key,
}) : child = null,
useSlivers = true,
isScrollable = true,
itemBuilder = null,
itemCount = null;

const BaseScreen.child({
required this.child,
this.actions,
this.onRefresh,
this.isLoading = false,
this.hasBottomSafeSpace = true,
this.onBackTapped,
this.title,
this.showHeader = true,
this.padding = const EdgeInsets.all(16),
this.background,
super.key,
}) : children = const [],
isScrollable = false,
useSlivers = false,
itemBuilder = null,
itemCount = null;

@override
Widget build(BuildContext context) {
return DataProviderWidget(
childBuilder: (context, theme, localization) => StatusBar.dark(
child: Scaffold(
backgroundColor: background ?? theme.colorsTheme.background,
body: GestureDetector(
onTap: FocusManager.instance.primaryFocus?.unfocus,
child: SafeArea(
top: !showHeader,
bottom: hasBottomSafeSpace,
child: Column(
children: [
if (showHeader) ...[
BaseScreenHeader(
title: title,
onBackTapped: onBackTapped,
trailingItems: actions ?? [],
),
],
Expanded(
child: Builder(
builder: (context) {
if (isLoading) {
return const Center(
child: FlutterTemplateProgressIndicator.dark(),
);
}

final content = BaseScreenContent(
isScrollable: isScrollable,
padding: padding,
useSlivers: useSlivers,
children: children,
child: child,
itemBuilder: itemBuilder,
itemCount: itemCount,
);
if (onRefresh == null) return content;

return RefreshIndicator(
onRefresh: onRefresh!,
child: content,
);
},
),
),
],
),
),
),
),
),
);
}
}
58 changes: 58 additions & 0 deletions lib/widget/general/simple_screen/base_screen_content.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';

class BaseScreenContent extends StatelessWidget {
final bool useSlivers;
final bool isScrollable;
final int? itemCount;
final Widget? child;
final EdgeInsets padding;
final List<Widget> children;
final IndexedWidgetBuilder? itemBuilder;

const BaseScreenContent({
required this.useSlivers,
required this.isScrollable,
required this.padding,
required this.children,
required this.itemBuilder,
required this.itemCount,
this.child,
super.key,
});

@override
Widget build(BuildContext context) {
final Widget content;

if (children.isNotEmpty) {
if (useSlivers) {
content = CustomScrollView(
slivers: children,
);
} else if (isScrollable) {
content = ListView(
padding: padding,
children: children,
);
} else {
content = Padding(
padding: padding,
child: Column(children: children),
);
}
} else if (itemBuilder != null) {
content = ListView.builder(
padding: padding,
itemBuilder: itemBuilder!,
itemCount: itemCount,
);
} else {
content = Padding(
padding: padding,
child: child,
);
}

return content;
}
}
49 changes: 49 additions & 0 deletions lib/widget/general/simple_screen/base_screen_header.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter_template/widget/general/styled/flutter_template_back_button.dart';
import 'package:flutter_template/widget/provider/data_provider_widget.dart';

class BaseScreenHeader extends StatelessWidget {
final String? title;
final List<Widget> trailingItems;
final VoidCallback? onBackTapped;

const BaseScreenHeader({
this.onBackTapped,
this.title,
this.trailingItems = const [],
super.key,
});

@override
Widget build(BuildContext context) {
return DataProviderWidget(
childBuilder: (context, theme, localization) => Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
color: theme.colorsTheme.primary,
child: SafeArea(
bottom: false,
child: Row(
children: [
if (ModalRoute.of(context)?.impliesAppBarDismissal ?? false) ...[
FlutterTemplateBackButton.light(onClick: onBackTapped),
const SizedBox(width: 24),
],
if (title != null) ...[
Expanded(
child: Text(
title!.toUpperCase(),
style: theme.inverseCoreTextTheme.bodyNormal,
),
),
],
...trailingItems,
],
),
),
),
);
}
}
37 changes: 37 additions & 0 deletions lib/widget/general/simple_screen/base_screen_header_safe_area.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';

class BaseScreenHeaderSafeArea extends StatelessWidget {
final Widget child;
final List<Widget> leading;
final List<Widget> actions;

const BaseScreenHeaderSafeArea({
required this.leading,
required this.actions,
required this.child,
super.key,
});

@override
Widget build(BuildContext context) {
final content = Opacity(
opacity: 0,
child: IgnorePointer(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
...leading,
...actions,
],
),
),
);
return Row(
children: [
content,
Expanded(child: child),
content,
],
);
}
}

0 comments on commit 47f8ca2

Please sign in to comment.