add breadcrumbs and allow clicking on disks
This commit is contained in:
parent
36523a1203
commit
ceeae0523e
127
app/lib/breadcrumb_page.dart
Normal file
127
app/lib/breadcrumb_page.dart
Normal 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
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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...");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user