Updating UI
Dieser Commit ist enthalten in:
Ursprung
9fc40df916
Commit
cdca1c8562
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:steamwar_multitool/src/screens/event.dart';
|
||||
import 'package:steamwar_multitool/src/screens/event_fights_graph.dart';
|
||||
|
||||
import 'screens/home.dart';
|
||||
import 'screens/login.dart';
|
||||
@ -23,6 +24,16 @@ final _routes = GoRouter(
|
||||
}
|
||||
return EditEventScreen(eventId);
|
||||
}),
|
||||
GoRoute(
|
||||
path: "/event/:eventId/graph",
|
||||
builder: (context, state) {
|
||||
final eventId = int.tryParse(state.params['eventId']!);
|
||||
if (eventId == null) {
|
||||
context.go("/");
|
||||
return const SizedBox();
|
||||
}
|
||||
return EventfightsGraph(eventId);
|
||||
}),
|
||||
],
|
||||
initialLocation: "/login",
|
||||
);
|
||||
|
99
lib/src/provider/mods.dart
Normale Datei
99
lib/src/provider/mods.dart
Normale Datei
@ -0,0 +1,99 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:steamwar_multitool/src/provider/http.dart';
|
||||
|
||||
final modRepository = FutureProvider(
|
||||
(ref) async => ModRepository(await ref.read(httpClient.future)),
|
||||
dependencies: [httpClient]);
|
||||
|
||||
final uncheckedMods = FutureProvider<List<Mod>>((ref) async {
|
||||
final repo = await ref.watch(modRepository.future);
|
||||
return repo.listUnchecked();
|
||||
}, dependencies: [modRepository]);
|
||||
|
||||
final mods = FutureProvider<List<Mod>>((ref) async {
|
||||
final repo = await ref.watch(modRepository.future);
|
||||
return repo.listMods();
|
||||
}, dependencies: [modRepository]);
|
||||
|
||||
class ModRepository {
|
||||
final Dio _client;
|
||||
|
||||
ModRepository(this._client);
|
||||
|
||||
Future<List<Mod>> listMods() async {
|
||||
final res = await _client.get("/mods/all");
|
||||
return (res.data as List).map((e) => Mod.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<List<Mod>> listUnchecked() async {
|
||||
final res = await _client.get("/mods/unchecked");
|
||||
return (res.data as List).map((e) => Mod.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<void> updateMod(Platform platform, String name, ModType type) {
|
||||
return _client.put("/mods/${platform.name}/$name", data: {
|
||||
"modType": type.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Mod {
|
||||
final Platform platform;
|
||||
final String name;
|
||||
final ModType type;
|
||||
|
||||
Mod(this.platform, this.name, this.type);
|
||||
|
||||
factory Mod.fromJson(Map<String, dynamic> json) {
|
||||
return Mod(
|
||||
Platform.fromOrdinal(json["platform"] as int),
|
||||
json["modName"],
|
||||
ModType.fromOrdinal(json["modType"] as int),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum Platform {
|
||||
FORGE,
|
||||
LABYMOD,
|
||||
FABRIC;
|
||||
|
||||
static Platform fromOrdinal(int ordinal) {
|
||||
switch (ordinal) {
|
||||
case 0:
|
||||
return FORGE;
|
||||
case 1:
|
||||
return LABYMOD;
|
||||
case 2:
|
||||
return FABRIC;
|
||||
default:
|
||||
throw Exception("Invalid ordinal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ModType {
|
||||
UNKLASSIFIED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
RED,
|
||||
YOUTUBER_ONLY;
|
||||
|
||||
static ModType fromOrdinal(int ordinal) {
|
||||
switch (ordinal) {
|
||||
case 0:
|
||||
return UNKLASSIFIED;
|
||||
case 1:
|
||||
return GREEN;
|
||||
case 2:
|
||||
return YELLOW;
|
||||
case 3:
|
||||
return RED;
|
||||
case 4:
|
||||
return YOUTUBER_ONLY;
|
||||
default:
|
||||
throw Exception("Invalid ordinal");
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,13 @@ class ErrorComponent extends StatelessWidget {
|
||||
return Container(
|
||||
color: Colors.red,
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
SelectableText(error.toString()),
|
||||
if (stackTrace != null) Text(stackTrace.toString()),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SelectableText(error.toString()),
|
||||
if (stackTrace != null) Text(stackTrace.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -22,7 +22,8 @@ class EventListComponent extends HookConsumerWidget {
|
||||
children: [
|
||||
FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
showDialog(context: context, builder: (context) => EventDialog());
|
||||
showDialog(
|
||||
context: context, builder: (context) => const EventDialog());
|
||||
},
|
||||
label: const Text("Create Event"),
|
||||
icon: const Icon(Icons.add),
|
||||
|
35
lib/src/screens/components/mod_list.dart
Normale Datei
35
lib/src/screens/components/mod_list.dart
Normale Datei
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:steamwar_multitool/src/provider/mods.dart';
|
||||
|
||||
import 'error.dart';
|
||||
|
||||
class ModListComponent extends HookConsumerWidget {
|
||||
const ModListComponent({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final list = ref.watch(uncheckedMods);
|
||||
|
||||
return list.when(
|
||||
data: (data) {
|
||||
return ListView(
|
||||
children: [
|
||||
Center(
|
||||
child: Text("WIP",
|
||||
style: Theme.of(context).textTheme.headline5)),
|
||||
...data
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text(e.name),
|
||||
subtitle: Text(e.platform.name),
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
],
|
||||
);
|
||||
},
|
||||
error: (err, stack) => ErrorComponent(err, stack),
|
||||
loading: () => const Center(child: CircularProgressIndicator()));
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:steamwar_multitool/src/util/event_fight_exporter.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
@ -302,7 +303,8 @@ class _EventScreen extends HookConsumerWidget {
|
||||
title: const Text("Use Spectate System"),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text("Teams", style: Theme.of(context).textTheme.headline6),
|
||||
Text("Teams (${eventData.teams.length})",
|
||||
style: Theme.of(context).textTheme.headline6),
|
||||
Wrap(
|
||||
children: [
|
||||
for (final team in eventData.teams)
|
||||
@ -340,6 +342,17 @@ class _EventFightList extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final event = eventData.event;
|
||||
final fights = useState<List<EventFight>>(eventData.fights);
|
||||
final selected = useState<List<int>>([]);
|
||||
|
||||
final currentCheckBoxState = useMemoized<bool?>(() {
|
||||
if (selected.value.isEmpty) {
|
||||
return false;
|
||||
} else if (selected.value.length == fights.value.length) {
|
||||
return true;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, [selected.value, fights.value]);
|
||||
|
||||
void update() async {
|
||||
final repo = await ref.read(eventRepositoryProvider.future);
|
||||
@ -351,30 +364,213 @@ class _EventFightList extends HookConsumerWidget {
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return _AddFightWidget(
|
||||
eventData,
|
||||
() => update(),
|
||||
);
|
||||
const SizedBox(width: 16),
|
||||
Checkbox(
|
||||
value: currentCheckBoxState,
|
||||
onChanged: (v) {
|
||||
if (v == null) {
|
||||
selected.value = [];
|
||||
} else if (v) {
|
||||
selected.value = fights.value
|
||||
.where((element) => element.start.isAfter(DateTime.now()))
|
||||
.map((e) => e.id)
|
||||
.toList();
|
||||
if (selected.value.isEmpty) {
|
||||
selected.value = fights.value.map((e) => e.id).toList();
|
||||
}
|
||||
} else {
|
||||
selected.value = fights.value.map((e) => e.id).toList();
|
||||
}
|
||||
},
|
||||
tristate: true,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ButtonBar(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: "Reschedule Fights",
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (selected.value.isEmpty) {
|
||||
return;
|
||||
}
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return HookBuilder(builder: (context) {
|
||||
final lowest = useMemoized<DateTime>(
|
||||
() => DateTime.fromMillisecondsSinceEpoch(
|
||||
fights.value
|
||||
.where((element) => selected.value
|
||||
.contains(element.id))
|
||||
.map((e) =>
|
||||
e.start.millisecondsSinceEpoch)
|
||||
.reduce(min)),
|
||||
[fights.value]);
|
||||
final date = useState<DateTime>(lowest);
|
||||
|
||||
final offset = useMemoized(
|
||||
() => date.value.difference(lowest),
|
||||
[date.value, lowest]);
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text("Reshedule Fights"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DateTimeEditor(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
(p0) {
|
||||
date.value = p0;
|
||||
},
|
||||
date.value,
|
||||
true,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"${offset.isNegative ? "-" : ""}${offset.inHours.abs().toString().padLeft(2, "0")}:${offset.inMinutes.remainder(60).abs().toInt().toString().padLeft(2, "0")}:${offset.inSeconds.remainder(60).abs().toInt().toString().padLeft(2, "0")}"),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final selectedFights = fights.value
|
||||
.where((element) => selected.value
|
||||
.contains(element.id))
|
||||
.toList();
|
||||
final repo = await ref
|
||||
.read(eventRepositoryProvider.future);
|
||||
for (final fight in selectedFights) {
|
||||
repo.updateFight(
|
||||
fight.id,
|
||||
fight.start.add(offset),
|
||||
fight.gameMode,
|
||||
fight.map,
|
||||
fight.kampfleiter.id,
|
||||
fight.blueTeam,
|
||||
fight.redTeam);
|
||||
}
|
||||
update();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Reshedule"),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
label: const Text("Add Fight"),
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
icon: const Icon(Icons.event_note),
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Change Kampfleiter",
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
if (selected.value.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final kampfleiter = await showSearch(
|
||||
context: context,
|
||||
delegate: UserSearchDelegate(
|
||||
await ref.read(usersProvider.future)));
|
||||
|
||||
if (kampfleiter == null) {
|
||||
return;
|
||||
} else {
|
||||
final repo =
|
||||
await ref.read(eventRepositoryProvider.future);
|
||||
for (final fight in fights.value) {
|
||||
if (selected.value.contains(fight.id)) {
|
||||
repo.updateFight(
|
||||
fight.id,
|
||||
fight.start,
|
||||
fight.gameMode,
|
||||
fight.map,
|
||||
kampfleiter.id,
|
||||
fight.blueTeam,
|
||||
fight.redTeam);
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.person)),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Delete Fights",
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (selected.value.isEmpty) {
|
||||
return;
|
||||
}
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text("Delete Fights"),
|
||||
content: const Text(
|
||||
"Do you really want to delete the selected fights?"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final repo = await ref
|
||||
.read(eventRepositoryProvider.future);
|
||||
for (final fight in selected.value) {
|
||||
repo.deleteFight(fight);
|
||||
}
|
||||
update();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Delete",
|
||||
style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.delete, color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return _AddFightWidget(
|
||||
eventData,
|
||||
() => update(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
label: const Text("Add Fight"),
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
return const [
|
||||
PopupMenuItem(
|
||||
child: const Text("Export Data"),
|
||||
value: "export",
|
||||
child: Text("Export Data"),
|
||||
),
|
||||
];
|
||||
},
|
||||
@ -386,7 +582,12 @@ class _EventFightList extends HookConsumerWidget {
|
||||
)
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
if (fights.value.isEmpty)
|
||||
const Center(
|
||||
child: Text("No fights yet"),
|
||||
),
|
||||
for (final fight in fights.value)
|
||||
ListTile(
|
||||
title: Text(
|
||||
@ -427,6 +628,17 @@ class _EventFightList extends HookConsumerWidget {
|
||||
},
|
||||
tileColor:
|
||||
fight.score != 0 ? fight.winner.color.withOpacity(0.5) : null,
|
||||
leading: Checkbox(
|
||||
value: selected.value.contains(fight.id),
|
||||
onChanged: (value) {
|
||||
if (value ?? false) {
|
||||
selected.value = [...selected.value, fight.id];
|
||||
} else {
|
||||
selected.value = selected.value
|
||||
.where((element) => element != fight.id)
|
||||
.toList();
|
||||
}
|
||||
}),
|
||||
)
|
||||
],
|
||||
);
|
||||
@ -474,7 +686,7 @@ class EditEventFightDialog extends HookConsumerWidget {
|
||||
const SizedBox(height: 8),
|
||||
DateTimeEditor((p0) {
|
||||
start.value = p0;
|
||||
}, start.value, true),
|
||||
}, start.value, true, mainAxisAlignment: MainAxisAlignment.center),
|
||||
const SizedBox(height: 8),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@ -540,6 +752,7 @@ class EditEventFightDialog extends HookConsumerWidget {
|
||||
title: const Text("Delete Fight?"),
|
||||
content: const Text(
|
||||
"Do you really want to delete this fight?"),
|
||||
icon: const Icon(Icons.warning),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
@ -561,7 +774,7 @@ class EditEventFightDialog extends HookConsumerWidget {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: const Text("Delete")),
|
||||
child: const Text("Delete", style: TextStyle(color: Colors.red))),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
@ -652,7 +865,8 @@ class _AddFightWidget extends HookConsumerWidget {
|
||||
_TeamSelector(event, redTeam.value, (p0) => redTeam.value = p0,
|
||||
eventData.teams),
|
||||
const SizedBox(height: 8),
|
||||
DateTimeEditor((p0) => date.value = p0, date.value, true),
|
||||
DateTimeEditor((p0) => date.value = p0, date.value, true,
|
||||
mainAxisAlignment: MainAxisAlignment.center),
|
||||
const SizedBox(height: 8),
|
||||
const Text("Kampfleiter"),
|
||||
const SizedBox(height: 8),
|
||||
@ -733,13 +947,15 @@ class DateTimeEditor extends HookConsumerWidget {
|
||||
final void Function(DateTime) onChanged;
|
||||
final DateTime initialDate;
|
||||
final bool enabled;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
const DateTimeEditor(this.onChanged, this.initialDate, this.enabled,
|
||||
{Key? key})
|
||||
{Key? key, this.mainAxisAlignment = MainAxisAlignment.start})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Row(
|
||||
mainAxisAlignment: mainAxisAlignment,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: enabled
|
||||
|
79
lib/src/screens/event_fights_graph.dart
Normale Datei
79
lib/src/screens/event_fights_graph.dart
Normale Datei
@ -0,0 +1,79 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:steamwar_multitool/src/provider/events.dart';
|
||||
|
||||
class EventfightsGraph extends HookConsumerWidget {
|
||||
final int eventId;
|
||||
const EventfightsGraph(this.eventId, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final eventFuture = useMemoized(() => ref
|
||||
.watch(eventRepositoryProvider.future)
|
||||
.then((value) => value.getEvent(eventId)));
|
||||
return FutureBuilder(
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return _EventFightsGraph(snapshot.data!);
|
||||
} else if (snapshot.hasError) {
|
||||
return const Text("Error");
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
future: eventFuture);
|
||||
}
|
||||
}
|
||||
|
||||
class EventFightPosition {
|
||||
final Offset position;
|
||||
final int fightId;
|
||||
|
||||
EventFightPosition(this.position, this.fightId);
|
||||
}
|
||||
|
||||
class _EventFightsGraph extends HookConsumerWidget {
|
||||
final EventExtended event;
|
||||
const _EventFightsGraph(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
//final positions = useState(List.generate(event.fights.length,
|
||||
// (index) => EventFightPosition(Offset.zero, event.fights[index].id)));
|
||||
final positions = useState([
|
||||
EventFightPosition(Offset.zero, 0),
|
||||
EventFightPosition(Offset.zero, 1)
|
||||
]);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(event.event.name),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
for (final fight in positions.value)
|
||||
Positioned(
|
||||
top: fight.position.dy,
|
||||
left: fight.position.dx,
|
||||
child: GestureDetector(
|
||||
onPanUpdate: (details) {
|
||||
positions.value = [
|
||||
...positions.value
|
||||
.where((element) => element.fightId != fight.fightId),
|
||||
EventFightPosition(
|
||||
fight.position + (details.delta * 2), fight.fightId)
|
||||
];
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
width: 100,
|
||||
height: 100,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -5,11 +5,13 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:steamwar_multitool/src/provider/mods.dart';
|
||||
|
||||
import '../provider/events.dart';
|
||||
import '../provider/server.dart';
|
||||
import '../provider/user.dart';
|
||||
import 'components/events_list.dart';
|
||||
import 'components/mod_list.dart';
|
||||
|
||||
class HomeScreen extends HookConsumerWidget {
|
||||
const HomeScreen({Key? key}) : super(key: key);
|
||||
@ -46,6 +48,7 @@ class HomeScreen extends HookConsumerWidget {
|
||||
? null
|
||||
: () {
|
||||
ref.invalidate(eventsListProvider);
|
||||
ref.invalidate(uncheckedMods);
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
@ -91,8 +94,8 @@ class HomeScreen extends HookConsumerWidget {
|
||||
label: Text('Events'),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.dns),
|
||||
label: Text('Server'),
|
||||
icon: Icon(Icons.developer_mode),
|
||||
label: Text('Mods'),
|
||||
),
|
||||
],
|
||||
labelType: NavigationRailLabelType.selected,
|
||||
@ -114,6 +117,7 @@ class HomeScreen extends HookConsumerWidget {
|
||||
//ServerListComponent(),
|
||||
EventListComponent(),
|
||||
Placeholder(),
|
||||
//ModListComponent(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@ -46,6 +47,10 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
icon: const Icon(Icons.save),
|
||||
),
|
||||
],
|
||||
leading: IconButton(
|
||||
onPressed: () => context.go("/"),
|
||||
icon: Icon(Icons.arrow_back),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren