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

View File

@@ -1,5 +1,5 @@
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:go_router/go_router.dart';
import 'package:provider/provider.dart';
@@ -13,16 +13,20 @@ class HomePage extends StatelessWidget {
appBar: AppBar(
title: Text("Splatournament"),
actions: [
Consumer<StateProvider>(
builder:
(BuildContext context, StateProvider value, Widget? child) {
return IconButton(
onPressed: () {
value.notifyState();
},
icon: Icon(Icons.refresh),
);
},
IconButton(
onPressed: () async {
final tournamentProvider =
Provider.of<TournamentProvider>(context, listen: false);
try {
await tournamentProvider.refreshAvailableTournaments();
} catch (_) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to refresh tournaments')),
);
}
},
icon: Icon(Icons.refresh),
),
PopupMenuButton(
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:frontend_splatournament_manager/models/tournament.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';
class AvailableTournamentList extends StatelessWidget {
@@ -17,13 +17,10 @@ class AvailableTournamentList extends StatelessWidget {
SizedBox(
width: double.infinity,
height: 350,
child: Consumer<StateProvider>(
builder:
(
BuildContext context,
StateProvider provider,
Widget? child,
) => TournamentListFutureBuilder(provider: provider),
child: Consumer<TournamentProvider>(
builder: (context, provider, _) {
return TournamentListFutureBuilder(provider: provider);
},
),
),
],
@@ -33,22 +30,30 @@ class AvailableTournamentList extends StatelessWidget {
}
class TournamentListFutureBuilder extends StatelessWidget {
final StateProvider provider;
final TournamentProvider provider;
const TournamentListFutureBuilder({super.key, required this.provider});
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: provider.fetchAvailableTournaments(),
return FutureBuilder<List<Tournament>>(
future: provider.ensureTournamentsLoaded(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.waiting) {
final list = provider.availableTournaments;
print(list);
if (snapshot.connectionState == ConnectionState.waiting &&
list.isEmpty) {
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(
shrinkWrap: false,
itemCount: list.length,
@@ -85,4 +90,3 @@ class TournamentListItem extends StatelessWidget {
);
}
}

View File

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