A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

182 lines
4.9 KiB

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:neon/blocs.dart';
import 'package:neon/theme.dart';
import 'package:neon/utils.dart';
import 'package:neon/widgets.dart';
import 'package:neon_dashboard/l10n/localizations.dart';
import 'package:neon_dashboard/src/blocs/dashboard.dart';
import 'package:neon_dashboard/src/widgets/dry_intrinsic_height.dart';
import 'package:neon_dashboard/src/widgets/widget.dart';
import 'package:neon_dashboard/src/widgets/widget_button.dart';
import 'package:neon_dashboard/src/widgets/widget_item.dart';
import 'package:nextcloud/dashboard.dart' as dashboard;
/// Displays the whole dashboard page layout.
class DashboardMainPage extends StatelessWidget {
/// Creates a new dashboard main page.
const DashboardMainPage({
super.key,
});
@override
Widget build(final BuildContext context) {
final bloc = NeonProvider.of<DashboardBloc>(context);
return ResultBuilder.behaviorSubject(
subject: bloc.widgets,
builder: (final context, final snapshot) {
Widget? child;
if (snapshot.hasData) {
var minHeight = 504.0;
final children = <Widget>[];
for (final widget in snapshot.requireData.entries) {
final items = buildWidgetItems(
context: context,
widget: widget.key,
items: widget.value,
).toList();
final height = items.map((final i) => i.height!).reduce((final a, final b) => a + b);
minHeight = max(minHeight, height);
children.add(
DashboardWidget(
widget: widget.key,
children: items,
),
);
}
child = Wrap(
alignment: WrapAlignment.center,
spacing: 8,
runSpacing: 8,
children: children
.map(
(final widget) => SizedBox(
width: 320,
height: minHeight + 24,
child: widget,
),
)
.toList(),
);
}
return Center(
child: NeonListView.custom(
scrollKey: 'dashboard',
isLoading: snapshot.isLoading,
error: snapshot.error,
onRefresh: bloc.refresh,
sliver: SliverFillRemaining(
hasScrollBody: false,
child: Center(
child: child,
),
),
),
);
},
);
}
/// Builds the list of messages, [items] and buttons for a [widget].
@visibleForTesting
static Iterable<SizedBox> buildWidgetItems({
required final BuildContext context,
required final dashboard.Widget widget,
required final dashboard.WidgetItems? items,
}) sync* {
yield SizedBox(
height: 64,
child: DryIntrinsicHeight(
child: ListTile(
title: Text(
widget.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
leading: SizedBox.square(
dimension: largeIconSize,
child: NeonUrlImage(
url: widget.iconUrl,
svgColorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
size: const Size.square(largeIconSize),
),
),
),
),
);
yield const SizedBox(
height: 20,
);
final halfEmptyContentMessage = _buildMessage(items?.halfEmptyContentMessage);
final emptyContentMessage = _buildMessage(items?.emptyContentMessage);
if (halfEmptyContentMessage != null) {
yield halfEmptyContentMessage;
}
if (emptyContentMessage != null) {
yield emptyContentMessage;
}
if (halfEmptyContentMessage == null && emptyContentMessage == null && (items?.items.isEmpty ?? true)) {
yield _buildMessage(DashboardLocalizations.of(context).noEntries)!;
}
if (items?.items != null) {
for (final item in items!.items) {
yield SizedBox(
height: 64,
child: DryIntrinsicHeight(
child: DashboardWidgetItem(
item: item,
roundIcon: widget.itemIconsRound,
),
),
);
}
}
yield const SizedBox(
height: 20,
);
if (widget.buttons != null) {
for (final button in widget.buttons!) {
yield SizedBox(
height: 32,
child: DashboardWidgetButton(
button: button,
),
);
}
}
}
static SizedBox? _buildMessage(final String? message) {
if (message == null || message.isEmpty) {
return null;
}
return SizedBox(
height: 60,
child: Center(
child: Column(
children: [
const Icon(
Icons.check,
size: largeIconSize,
),
Text(message),
],
),
),
);
}
}