add breadcrumbs and allow clicking on disks

This commit is contained in:
lukas-heiligenbrunner 2022-12-09 01:45:00 +01:00
parent 36523a1203
commit ceeae0523e
6 changed files with 251 additions and 49 deletions

View File

@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:provider/provider.dart';
class _BreadCrumbNode {
String name;
Widget widget;
_BreadCrumbNode? next;
_BreadCrumbNode(this.name, this.widget);
}
class BreadCrumbController extends ChangeNotifier {
final Widget defaultRoute;
final String rootName;
late _BreadCrumbNode _rootPage = _BreadCrumbNode(rootName, defaultRoute);
late Widget _currentpage = _rootPage.widget;
void pushPage(Widget widget, String name) {
_last(_rootPage).next = _BreadCrumbNode(name, widget);
_currentpage = widget;
notifyListeners();
}
void switchPage(String name) {
final pageNode = _findPage(name, _rootPage);
if (pageNode != null) {
_currentpage = pageNode.widget;
// hope for garbage collector to free the floating items (;
pageNode.next = null;
}
notifyListeners();
}
List<String> getNames() {
return _genNameList(_rootPage);
}
Widget getCurrentPage() {
return _currentpage;
}
_BreadCrumbNode _last(_BreadCrumbNode node) {
if (node.next != null) {
return _last(node.next!);
} else {
return node;
}
}
_BreadCrumbNode? _findPage(String name, _BreadCrumbNode node) {
if (node.name == name) {
return node;
}
if (node.next != null) {
return _findPage(name, node.next!);
} else {
return null;
}
}
List<String> _genNameList(_BreadCrumbNode node) {
if (node.next != null) {
return _genNameList(node.next!)..insert(0, node.name);
} else {
return [node.name];
}
}
BreadCrumbController(this.defaultRoute, this.rootName);
}
class BreadCrumbPage extends StatefulWidget {
const BreadCrumbPage(
{Key? key, required this.mainPage, required this.rootName})
: super(key: key);
final Widget mainPage;
final String rootName;
@override
State<BreadCrumbPage> createState() => _BreadCrumbPageState();
}
class _BreadCrumbPageState extends State<BreadCrumbPage> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<BreadCrumbController>(
create: (BuildContext context) =>
BreadCrumbController(widget.mainPage, widget.rootName),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 12, top: 8, bottom: 8),
child: Consumer<BreadCrumbController>(
builder: (_, controller, __) => BreadCrumb(
items: controller
.getNames()
.map((e) => BreadCrumbItem(
padding: const EdgeInsets.all(2),
content: Text(e,
style: Theme.of(context).textTheme.labelMedium),
onTap: () {
controller.switchPage(e);
},
))
.toList(growable: false),
divider: Icon(
Icons.chevron_right,
color: Theme.of(context).textTheme.labelMedium?.color,
),
),
),
),
Divider(color: Colors.white.withOpacity(0.3), height: 1),
Consumer<BreadCrumbController>(
builder: (_, value, __) => value.getCurrentPage(),
)
// currentPage
],
),
);
}
}

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:raid_manager/api/request.dart'; import 'package:raid_manager/api/request.dart';
import 'package:raid_manager/breadcrumb_page.dart';
import 'package:raid_manager/types/disk.dart'; import 'package:raid_manager/types/disk.dart';
import 'package:raid_manager/utils/file_formatter.dart'; import 'package:raid_manager/utils/file_formatter.dart';
@ -12,42 +14,67 @@ class DiskPage extends StatefulWidget {
class _DiskPageState extends State<DiskPage> { class _DiskPageState extends State<DiskPage> {
Future<List<Disk>> fetchDisks() async { Future<List<Disk>> fetchDisks() async {
return (await getJson('/api/disks') as List) return ((await getJson('/api/disks')) as List)
.map((e) => Disk.fromJson(e)) .map((e) => Disk.fromJson(e))
.toList(growable: false); .toList(growable: false);
} }
late final myFetch = fetchDisks(); @override
void initState() {
super.initState();
myFetch = fetchDisks();
}
late Future<List<Disk>> myFetch;
Widget _buildMainPage() {
return Expanded(
child: FutureBuilder(
future: myFetch,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
print(snapshot.error);
return const Text("errored");
} else if (snapshot.hasData) {
final data = snapshot.data!;
return ListView.builder(
itemCount: data.length,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, idx) {
return ListTile(
title: Text(
data[idx].name,
style: Theme.of(context).textTheme.headlineMedium,
),
subtitle: Text(
data[idx].size.readableFileSize(),
style: Theme.of(context).textTheme.labelMedium,
),
onTap: () {
Provider.of<BreadCrumbController>(context, listen: false)
.pushPage(
const Text("Mysupercool page"), data[idx].name);
},
);
},
);
} else {
return const Text("loading...");
}
},
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder( return BreadCrumbPage(
future: myFetch, mainPage: _buildMainPage(),
builder: (context, snapshot) { rootName: "Disks",
if (snapshot.hasData) {
final data = snapshot.data!;
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, idx) {
return ListTile(
title: Text(
data[idx].name,
style: Theme.of(context).textTheme.headlineMedium,
),
subtitle: Text(
data[idx].size.readableFileSize(),
style: Theme.of(context).textTheme.labelMedium,
),
onTap: () {},
);
},
);
} else if (snapshot.hasError) {
return const Text("errored");
} else {
return const Text("loading...");
}
},
); );
} }
} }

View File

@ -4,6 +4,7 @@ import 'package:raid_manager/raid_page.dart';
import 'package:sidebarx/sidebarx.dart'; import 'package:sidebarx/sidebarx.dart';
void main() { void main() {
print("starting");
runApp(SidebarXExampleApp()); runApp(SidebarXExampleApp());
} }

View File

@ -24,27 +24,44 @@ class _RaidPageState extends State<RaidPage> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
final data = snapshot.data!; final data = snapshot.data!;
return ListView.builder(
itemCount: data.raids.length, if (data.raids.isEmpty) {
itemBuilder: (context, idx) { return Column(
return ListTile( mainAxisAlignment: MainAxisAlignment.center,
title: Text( children: [
data.raids[idx].name, Icon(Icons.warning_amber_rounded,
style: Theme.of(context).textTheme.headlineMedium, size: 42, color: Colors.white.withOpacity(.6)),
const SizedBox(
height: 7,
), ),
subtitle: Text( Text("No Raid available",
"${data.raids[idx].level} - ${data.raids[idx].faulty ? "errored" : "active sync"}", style: Theme.of(context).textTheme.headlineMedium)
style: Theme.of(context).textTheme.labelMedium, ],
), );
onTap: () { } else {
Navigator.push( return ListView.builder(
context, itemCount: data.raids.length,
MaterialPageRoute( itemBuilder: (context, idx) {
builder: (_) => RaidInfoPage(raid: data.raids[idx]))); return ListTile(
}, title: Text(
); data.raids[idx].name,
}, style: Theme.of(context).textTheme.headlineMedium,
); ),
subtitle: Text(
"${data.raids[idx].level} - ${data.raids[idx].faulty ? "errored" : "active sync"}",
style: Theme.of(context).textTheme.labelMedium,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
RaidInfoPage(raid: data.raids[idx])));
},
);
},
);
}
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return const Text("errored"); return const Text("errored");
} else { } else {

View File

@ -181,6 +181,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_breadcrumb:
dependency: "direct dev"
description:
name: flutter_breadcrumb
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -305,6 +312,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.3"
nested:
dependency: transitive
description:
name: nested
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@ -319,6 +333,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.2" version: "1.8.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -326,6 +347,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
provider:
dependency: "direct dev"
description:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.4"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:

View File

@ -52,6 +52,8 @@ dev_dependencies:
flutter_lints: ^2.0.0 flutter_lints: ^2.0.0
build_runner: build_runner:
json_serializable: json_serializable:
flutter_breadcrumb: ^1.0.1
provider:
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec