Skip to content

Commit 5542966

Browse files
committed
feat: paginated list view
1 parent 3d946fd commit 5542966

37 files changed

+901
-46
lines changed

lib/animations/helpers.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
part of devspace;
2+
3+
4+
class TweenAnimationLoopBuilder<T extends Object?> extends StatefulWidget {
5+
6+
final Tween<T> tween;
7+
final Duration duration;
8+
final Curve curve;
9+
final bool loopWithReverse;
10+
final ValueWidgetBuilder<T> builder;
11+
12+
const TweenAnimationLoopBuilder({
13+
super.key,
14+
required this.tween,
15+
required this.duration,
16+
this.curve = Curves.linear,
17+
this.loopWithReverse = true,
18+
required this.builder
19+
});
20+
21+
@override
22+
State<TweenAnimationLoopBuilder> createState() => _TweeLoopnBuilderState<T>();
23+
}
24+
25+
class _TweeLoopnBuilderState<T extends Object?> extends State<TweenAnimationLoopBuilder<T>> {
26+
27+
bool _animatingIn = true;
28+
Key _keyAnimationWidget = GlobalKey();
29+
final Key _keyChild = GlobalKey();
30+
31+
@override
32+
Widget build(BuildContext context)
33+
{
34+
return TweenAnimationBuilder<T>(
35+
key: _keyAnimationWidget,
36+
tween: _animatingIn || !widget.loopWithReverse ? widget.tween : ReverseTween(widget.tween),
37+
duration: widget.duration,
38+
curve: widget.curve,
39+
builder: (context, value, _)
40+
{
41+
return KeyedSubtree(
42+
key: _keyChild,
43+
child: widget.builder(context, value, null),
44+
);
45+
},
46+
onEnd: ()
47+
{
48+
if (!context.mounted) return;
49+
50+
setState(()
51+
{
52+
_animatingIn = !_animatingIn;
53+
_keyAnimationWidget = GlobalKey();
54+
});
55+
},
56+
);
57+
}
58+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
3+
4+
part of devspace;
5+
6+
7+
typedef FetchPageItems<T> = Future<PageFetchResult<T>> Function(int pageSize, String? pageCursor);
8+
9+
class PaginatedListView<T> extends StatefulWidget {
10+
11+
final EdgeInsetsGeometry? padding;
12+
final IndexedWidgetBuilder? separatorBuilder;
13+
final int pageSize;
14+
final FetchPageItems<T> fetchPageItems;
15+
final IndexItemBuilder<T> buildItem;
16+
17+
const PaginatedListView({
18+
super.key,
19+
this.pageSize = 20,
20+
this.padding,
21+
this.separatorBuilder,
22+
required this.fetchPageItems,
23+
required this.buildItem
24+
});
25+
26+
@override
27+
State<PaginatedListView> createState() => _PaginatedListViewState<T>();
28+
}
29+
30+
const String _pagninatedListStartCursor = 'INIT';
31+
32+
class _PaginatedListViewState<T> extends State<PaginatedListView<T>> {
33+
34+
final PagingController<String, T> _controller = PagingController(firstPageKey: _pagninatedListStartCursor);
35+
36+
@override
37+
void initState()
38+
{
39+
_controller.addPageRequestListener(_fetchPage);
40+
super.initState();
41+
}
42+
43+
Future<void> _fetchPage(String pageKey) async
44+
{
45+
try
46+
{
47+
String? pageCursor = pageKey == _pagninatedListStartCursor ? null : pageKey;
48+
49+
final fetchResult = await widget.fetchPageItems(widget.pageSize, pageCursor);
50+
51+
final isLastPage = fetchResult.items.length < widget.pageSize || fetchResult.nextCursor == null;
52+
53+
if (isLastPage)
54+
{
55+
_controller.appendLastPage(fetchResult.items);
56+
}
57+
else
58+
{
59+
_controller.appendPage(fetchResult.items, fetchResult.nextCursor);
60+
}
61+
}
62+
catch (error)
63+
{
64+
_controller.error = error;
65+
}
66+
}
67+
68+
@override
69+
Widget build(BuildContext context)
70+
{
71+
return PagedListView<String, T>.separated(
72+
pagingController: _controller,
73+
physics: context.animations.scrollPhysics,
74+
padding: widget.padding,
75+
separatorBuilder: (context, index) => widget.separatorBuilder?.call(context, index) ?? const SizedBox.shrink(),
76+
builderDelegate: PagedChildBuilderDelegate<T>(
77+
itemBuilder: widget.buildItem,
78+
firstPageErrorIndicatorBuilder: (_) => Center(
79+
child: ArchInfoBox.error(
80+
variant: kInfoBoxVariant.contentPlaceholder,
81+
icon: Symbols.error_rounded,
82+
title: LibStrings.lib_general_titleError.tr(),
83+
message: LibStrings.lib_general_errorUnknown.tr(),
84+
onAction: () => _controller.refresh(),
85+
),
86+
),
87+
noItemsFoundIndicatorBuilder: (_) => Center(
88+
child: ArchInfoBox.info(
89+
variant: kInfoBoxVariant.contentPlaceholder,
90+
icon: Symbols.format_list_bulleted_rounded,
91+
title: LibStrings.lib_general_titleInfo.tr(),
92+
message: LibStrings.lib_general_infoNoItemsFound.tr(),
93+
onAction: () => _controller.refresh(),
94+
),
95+
),
96+
firstPageProgressIndicatorBuilder: (_) => const Center(child: ArchLoadingIndicator(variant: kLoadingIndicatorVariant.special)),
97+
newPageProgressIndicatorBuilder: (_) => const Center(child: ArchLoadingIndicator()),
98+
// TODO: add all 3
99+
// newPageErrorIndicatorBuilder: (_) => NewPageErrorIndicator(
100+
// error: _controller.error,
101+
// onTryAgain: () => _controller.retryLastFailedRequest(),
102+
// ),
103+
// noMoreItemsIndicatorBuilder: (_) => NoMoreItemsIndicator(),
104+
),
105+
);
106+
}
107+
108+
@override
109+
void dispose()
110+
{
111+
_controller.dispose();
112+
super.dispose();
113+
}
114+
}

lib/app_flow_systems/steps_bar.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ class StepsBar extends StatelessWidget {
4444

4545
if (specialFinalNextButton)
4646
{
47-
return LibStrings.lib_general_complete.tr();
47+
return LibStrings.lib_general_actionComplete.tr();
4848
}
4949

5050
return _nextButtonTitle();
5151
}
5252

5353
String _nextButtonTitle()
5454
{
55-
return customNextButtonTitle ?? LibStrings.lib_general_next.tr();
55+
return customNextButtonTitle ?? LibStrings.lib_general_actionNext.tr();
5656
}
5757

5858
String _backButtonTitle()
@@ -64,10 +64,10 @@ class StepsBar extends StatelessWidget {
6464
return '';
6565
}
6666

67-
return customCancelButtonTitle ?? LibStrings.lib_general_cancel.tr();
67+
return customCancelButtonTitle ?? LibStrings.lib_general_actionCancel.tr();
6868
}
6969

70-
return customBackButtonTitle ?? LibStrings.lib_general_back.tr();
70+
return customBackButtonTitle ?? LibStrings.lib_general_actionBack.tr();
7171
}
7272

7373
bool _finalNextButtonDiffersInAnyWay()

lib/architect/architect_plans.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ part of devspace;
44

55
Map<Type, WidgetPlan> _defaultWidgetPlans = <Type, WidgetPlan>{
66

7-
ButtonData: WidgetPlan<kButtonVariant, ButtonData>(builder: DefaultButton.builder),
8-
InfoBoxData: WidgetPlan<kInfoBoxVariant, InfoBoxData>(builder: DefaultInfoBox.builder),
9-
LoadingIndicatorData: WidgetPlan<kLoadingIndicatorVariant, LoadingIndicatorData>(builder: DefaultLoadingIndicator.builder),
10-
TextFieldData: WidgetPlan<kTextFieldVariant, TextFieldData>(builder: DefaultTextField.builder),
7+
ButtonData: WidgetPlanChildless<kButtonVariant, ButtonData>(builder: DefaultButton.builder),
8+
CardData: WidgetPlanChildful<kCardVariant, CardData, Widget>(builder: DefaultCard.builder),
9+
InfoBoxData: WidgetPlanChildless<kInfoBoxVariant, InfoBoxData>(builder: DefaultInfoBox.builder),
10+
LoadingIndicatorData: WidgetPlanChildless<kLoadingIndicatorVariant, LoadingIndicatorData>(builder: DefaultLoadingIndicator.builder),
11+
TextFieldData: WidgetPlanChildless<kTextFieldVariant, TextFieldData>(builder: DefaultTextField.builder),
1112

1213
};
1314

15+
1416
Map<Type, ObjectPlan> _defaultObjectPlans = <Type, ObjectPlan>{
1517

1618
InputDecorationData: ObjectPlan<InputDecoration, kNoVariants, InputDecorationData>(builder: DefaultInputDecoration.builder),
@@ -24,11 +26,11 @@ class ArchitectPlans {
2426
return const ArchitectPlans();
2527
}
2628

27-
final Map<Type, WidgetPlan> _widgets;
29+
final Map<Type, WidgetPlanChildless> _widgets;
2830
final Map<Type, ObjectPlan> _objects;
2931

3032
const ArchitectPlans({
31-
Map<Type, WidgetPlan>? widgets,
33+
Map<Type, WidgetPlanChildless>? widgets,
3234
Map<Type, ObjectPlan>? objects,
3335
}) :
3436
_widgets = widgets ?? const {},

lib/architect/materials/arch_base.dart

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,57 @@ class ArchBase<V extends Enum, T extends PlanData> extends StatelessWidget {
1818
@override
1919
Widget build(BuildContext context)
2020
{
21-
WidgetPlan<V, T> plan = Arch.of(context).planForWidget<T>() as WidgetPlan<V, T>;
21+
WidgetPlanChildless<V, T> plan = Arch.of(context).planForWidget<T>() as WidgetPlanChildless<V, T>;
2222

2323
return plan.builder(context, variant, data);
2424
}
2525
}
2626

27+
class ArchBaseWithChildren<V extends Enum, T extends PlanData> extends StatelessWidget {
28+
29+
final V variant;
30+
final T data;
31+
final List<Widget> children;
32+
33+
const ArchBaseWithChildren({
34+
super.key,
35+
required this.data,
36+
required this.variant,
37+
required this.children,
38+
});
39+
40+
@override
41+
Widget build(BuildContext context)
42+
{
43+
WidgetPlanChildful<V, T, List<Widget>> plan = Arch.of(context).planForWidget<T>() as WidgetPlanChildful<V, T, List<Widget>>;
44+
45+
return plan.builder(context, variant, data, children);
46+
}
47+
}
48+
49+
class ArchBaseWithChild<V extends Enum, T extends PlanData> extends StatelessWidget {
50+
51+
final V variant;
52+
final T data;
53+
final Widget child;
54+
55+
const ArchBaseWithChild({
56+
super.key,
57+
required this.data,
58+
required this.variant,
59+
required this.child,
60+
});
61+
62+
@override
63+
Widget build(BuildContext context)
64+
{
65+
WidgetPlanChildful<V, T, Widget> plan = Arch.of(context).planForWidget<T>() as WidgetPlanChildful<V, T, Widget>;
66+
67+
return plan.builder(context, variant, data, child);
68+
}
69+
}
70+
71+
2772
class ArchObjectBase<R extends Object, V extends Enum, T extends PlanData> {
2873

2974
final V variant;

lib/architect/materials/arch_base_stateless_widget.dart

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55

66
part of devspace;
77

8+
abstract class ArchBaseChildfulStatelessWidget<T extends PlanData, C> extends ArchBaseStatelessWidget<T> {
9+
10+
final C child;
11+
12+
ArchBaseChildfulStatelessWidget({
13+
super.key,
14+
required super.data,
15+
required this.child,
16+
super.allowCustomVariants,
17+
});
18+
}
19+
820
abstract class ArchBaseStatelessWidget<T extends PlanData> extends StatelessWidget {
921

1022
final T data;
@@ -44,14 +56,14 @@ abstract class ArchBaseStatelessWidget<T extends PlanData> extends StatelessWidg
4456
return;
4557
}
4658

47-
debugPrint('The following fields are not supported by the widget:\n');
59+
debugPrint('[WARNING]\nArchitect Grumbles - Not constructed as planned!\nThe following fields are not supported by the widget $this:\n|\n');
4860

4961
for (var element in _unsupportedFields)
5062
{
51-
debugPrint('- $element');
63+
debugPrint('\t- $element');
5264
}
5365

54-
debugPrint('\nIf you want to use these fields, you have to create create your own widget or and make sure it is not marked as unsupported.');
66+
debugPrint('\n|\nIf you want to use these fields, you have to create create your own widget and/or and make sure it is not marked as unsupported.');
5567
}
5668

5769

lib/architect/materials/arch_base_variant_switch.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,30 @@ abstract class ArchBaseVariantSwitch<V extends Enum, T extends PlanData> extends
2727

2828

2929

30+
}
31+
32+
abstract class ArchBaseChildfulVariantSwitch<V extends Enum, T extends PlanData, C> extends StatelessWidget {
33+
34+
final V variant;
35+
final T data;
36+
final C child;
37+
38+
const ArchBaseChildfulVariantSwitch({
39+
super.key,
40+
required this.variant,
41+
required this.data,
42+
required this.child
43+
});
44+
45+
@override
46+
@nonVirtual
47+
Widget build(BuildContext context)
48+
{
49+
return buildStyle(context, variant, child);
50+
}
51+
52+
Widget buildStyle(BuildContext context, V style, C child);
53+
54+
55+
3056
}

0 commit comments

Comments
 (0)