Separate Providers

This commit is contained in:
2026-03-07 00:27:55 +01:00
parent 89e6d6cd83
commit 67d4288c84
7 changed files with 99 additions and 72 deletions

View File

@@ -3,7 +3,8 @@ import 'package:frontend_splatournament_manager/pages/home_page.dart';
import 'package:frontend_splatournament_manager/pages/login_page.dart'; import 'package:frontend_splatournament_manager/pages/login_page.dart';
import 'package:frontend_splatournament_manager/pages/settings_page.dart'; import 'package:frontend_splatournament_manager/pages/settings_page.dart';
import 'package:frontend_splatournament_manager/providers/auth_provider.dart'; import 'package:frontend_splatournament_manager/providers/auth_provider.dart';
import 'package:frontend_splatournament_manager/state_provider.dart'; import 'package:frontend_splatournament_manager/providers/theme_provider.dart';
import 'package:frontend_splatournament_manager/providers/tournament_provider.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -11,7 +12,8 @@ void main() {
runApp( runApp(
MultiProvider( MultiProvider(
providers: [ providers: [
ChangeNotifierProvider(create: (_) => StateProvider()), ChangeNotifierProvider(create: (_) => ThemeProvider()),
ChangeNotifierProvider(create: (_) => TournamentProvider()),
ChangeNotifierProvider(create: (_) => AuthProvider()), ChangeNotifierProvider(create: (_) => AuthProvider()),
], ],
child: const SplatournamentApp(), child: const SplatournamentApp(),
@@ -23,11 +25,11 @@ class SplatournamentApp extends StatelessWidget {
const SplatournamentApp({super.key}); const SplatournamentApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final stateProvider = Provider.of<StateProvider>(context); final themeProvider = Provider.of<ThemeProvider>(context);
return MaterialApp.router( return MaterialApp.router(
title: 'Splatournament Manager', title: 'Splatournament Manager',
routerConfig: routes, routerConfig: routes,
themeMode: stateProvider.theme, themeMode: themeProvider.theme,
theme: ThemeData( theme: ThemeData(
brightness: Brightness.light, brightness: Brightness.light,
primarySwatch: Colors.blue, primarySwatch: Colors.blue,

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend_splatournament_manager/state_provider.dart'; import 'package:frontend_splatournament_manager/providers/tournament_provider.dart';
import 'package:frontend_splatournament_manager/widgets/available_tournament_list.dart'; import 'package:frontend_splatournament_manager/widgets/available_tournament_list.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -13,16 +13,20 @@ class HomePage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text("Splatournament"), title: Text("Splatournament"),
actions: [ actions: [
Consumer<StateProvider>( IconButton(
builder: onPressed: () async {
(BuildContext context, StateProvider value, Widget? child) { final tournamentProvider =
return IconButton( Provider.of<TournamentProvider>(context, listen: false);
onPressed: () { try {
value.notifyState(); await tournamentProvider.refreshAvailableTournaments();
}, } catch (_) {
icon: Icon(Icons.refresh), if (!context.mounted) return;
); ScaffoldMessenger.of(context).showSnackBar(
}, SnackBar(content: Text('Failed to refresh tournaments')),
);
}
},
icon: Icon(Icons.refresh),
), ),
PopupMenuButton( PopupMenuButton(
onSelected: (value) { onSelected: (value) {

View File

@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class ThemeProvider extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get theme => _themeMode;
void setTheme(ThemeMode mode) {
_themeMode = mode;
notifyListeners();
}
}

View File

@@ -0,0 +1,41 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:frontend_splatournament_manager/models/tournament.dart';
import 'package:http/http.dart' as http;
class TournamentProvider extends ChangeNotifier {
static const String baseUrl = "http://10.0.2.2:3000";
List<Tournament> _availableTournaments = [];
Future<List<Tournament>>? _initialLoadFuture;
List<Tournament> get availableTournaments => _availableTournaments;
Future<List<Tournament>> _fetchTournaments() async {
final response = await http.get(Uri.parse('$baseUrl/tournaments'));
if (response.statusCode != HttpStatus.ok) {
throw Exception('Failed to load tournaments (${response.statusCode})');
}
final List<dynamic> list = json.decode(response.body);
return list.map((json) => Tournament.fromJson(json)).toList();
}
Future<List<Tournament>> fetchAvailableTournaments() async {
_availableTournaments = await _fetchTournaments();
notifyListeners();
return _availableTournaments;
}
Future<List<Tournament>> ensureTournamentsLoaded() {
_initialLoadFuture ??= fetchAvailableTournaments();
return _initialLoadFuture!;
}
Future<List<Tournament>> refreshAvailableTournaments() {
_initialLoadFuture = fetchAvailableTournaments();
return _initialLoadFuture!;
}
}

View File

@@ -1,36 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend_splatournament_manager/models/tournament.dart';
import 'package:http/http.dart' as http;
class StateProvider extends ChangeNotifier {
static const String baseUrl = "http://10.0.2.2:3000";
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get theme => _themeMode;
void setTheme(ThemeMode mode) {
_themeMode = mode;
notifyListeners();
}
List<Tournament>? _availableTournaments;
void notifyState(){
notifyListeners();
}
Future<List<Tournament>> fetchAvailableTournaments() async {
try {
var response = await http.get(Uri.parse('$baseUrl/tournaments'));
if (response.statusCode == 200) {
final List<dynamic> list = json.decode(response.body);
_availableTournaments = list.map((json) => Tournament.fromJson(json)).toList();
return _availableTournaments!;
}
} catch (e) {
_availableTournaments = null;
return Future.error(e);
}
return[];
}
List<Tournament> get availableTournaments => _availableTournaments ?? [];
}

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend_splatournament_manager/models/tournament.dart'; import 'package:frontend_splatournament_manager/models/tournament.dart';
import 'package:frontend_splatournament_manager/pages/tournament_detail_page.dart'; import 'package:frontend_splatournament_manager/pages/tournament_detail_page.dart';
import 'package:frontend_splatournament_manager/state_provider.dart'; import 'package:frontend_splatournament_manager/providers/tournament_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AvailableTournamentList extends StatelessWidget { class AvailableTournamentList extends StatelessWidget {
@@ -17,13 +17,10 @@ class AvailableTournamentList extends StatelessWidget {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
height: 350, height: 350,
child: Consumer<StateProvider>( child: Consumer<TournamentProvider>(
builder: builder: (context, provider, _) {
( return TournamentListFutureBuilder(provider: provider);
BuildContext context, },
StateProvider provider,
Widget? child,
) => TournamentListFutureBuilder(provider: provider),
), ),
), ),
], ],
@@ -33,22 +30,30 @@ class AvailableTournamentList extends StatelessWidget {
} }
class TournamentListFutureBuilder extends StatelessWidget { class TournamentListFutureBuilder extends StatelessWidget {
final StateProvider provider; final TournamentProvider provider;
const TournamentListFutureBuilder({super.key, required this.provider}); const TournamentListFutureBuilder({super.key, required this.provider});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder( return FutureBuilder<List<Tournament>>(
future: provider.fetchAvailableTournaments(), future: provider.ensureTournamentsLoaded(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { final list = provider.availableTournaments;
return Center(child: Text('Error: ${snapshot.error}')); print(list);
} else if (!snapshot.hasData || if (snapshot.connectionState == ConnectionState.waiting &&
snapshot.connectionState == ConnectionState.waiting) { list.isEmpty) {
return Center(child: CircularProgressIndicator()); return Center(child: CircularProgressIndicator());
} }
var list = snapshot.data!;
if (snapshot.hasError && list.isEmpty) {
return Center(child: Text('Error: ${snapshot.error}'));
}
if (list.isEmpty) {
return Center(child: Text('No tournaments found'));
}
return ListView.builder( return ListView.builder(
shrinkWrap: false, shrinkWrap: false,
itemCount: list.length, itemCount: list.length,
@@ -85,4 +90,3 @@ class TournamentListItem extends StatelessWidget {
); );
} }
} }

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../state_provider.dart'; import '../providers/theme_provider.dart';
class ThemeSelectorWidget extends StatelessWidget { class ThemeSelectorWidget extends StatelessWidget {
ThemeSelectorWidget({super.key}); ThemeSelectorWidget({super.key});
@@ -23,7 +23,7 @@ class ThemeSelectorWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final stateProvider = Provider.of<StateProvider>(context); final themeProvider = Provider.of<ThemeProvider>(context);
return Container( return Container(
decoration: BoxDecoration(color: Theme.of(context).hoverColor, borderRadius: BorderRadius.circular(8)), decoration: BoxDecoration(color: Theme.of(context).hoverColor, borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
@@ -37,10 +37,10 @@ class ThemeSelectorWidget extends StatelessWidget {
child: DropdownButtonFormField( child: DropdownButtonFormField(
icon: Icon(Icons.color_lens), icon: Icon(Icons.color_lens),
items: dropdownElements, items: dropdownElements,
initialValue: stateProvider.theme, initialValue: themeProvider.theme,
onChanged: (value) { onChanged: (value) {
if (value == null) return; if (value == null) return;
stateProvider.setTheme(value); themeProvider.setTheme(value);
}, },
), ),
), ),