layout improvements
This commit is contained in:
@@ -69,15 +69,6 @@ class CommunityDecksScreenState extends State<CommunityDecksScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _load() async {
|
Future<void> _load() async {
|
||||||
final user = ApiAuthService.instance.currentUser.value;
|
|
||||||
if (user == null) {
|
|
||||||
setState(() {
|
|
||||||
_loading = false;
|
|
||||||
_decks = [];
|
|
||||||
_error = null;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_loading = true;
|
_loading = true;
|
||||||
_error = null;
|
_error = null;
|
||||||
@@ -179,34 +170,6 @@ class CommunityDecksScreenState extends State<CommunityDecksScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final user = ApiAuthService.instance.currentUser.value;
|
final user = ApiAuthService.instance.currentUser.value;
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
final body = Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Log in to browse and add community decks.',
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () => Navigator.pushNamed(context, Routes.login)
|
|
||||||
.then((_) => _load()),
|
|
||||||
child: const Text('Log in'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (widget.showAppBar) {
|
|
||||||
return Scaffold(appBar: AppBar(title: const Text('Community decks')), body: body);
|
|
||||||
}
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
final body = _loading
|
final body = _loading
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: _error != null
|
: _error != null
|
||||||
@@ -270,11 +233,48 @@ class CommunityDecksScreenState extends State<CommunityDecksScreen> {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Row(
|
||||||
'${d.questionCount} questions'
|
children: [
|
||||||
'${d.ownerDisplayName != null ? ' · ${d.ownerDisplayName}' : ''}',
|
Icon(
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
Icons.person_outline,
|
||||||
|
size: 14,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
d.ownerDisplayName != null && d.ownerDisplayName!.isNotEmpty
|
||||||
|
? d.ownerDisplayName!
|
||||||
|
: 'Unknown',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${d.questionCount} questions',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
if (d.averageRating != null) ...[
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Icon(
|
||||||
|
Icons.star,
|
||||||
|
size: 14,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
Text(
|
||||||
|
d.ratingCount != null && d.ratingCount! > 0
|
||||||
|
? '${d.averageRating!.toStringAsFixed(1)} (${d.ratingCount})'
|
||||||
|
: d.averageRating!.toStringAsFixed(1),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -287,10 +287,18 @@ class CommunityDecksScreenState extends State<CommunityDecksScreen> {
|
|||||||
vertical: 4,
|
vertical: 4,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: FilledButton.tonal(
|
: user == null
|
||||||
onPressed: () => _addToMyDecks(d),
|
? FilledButton.tonal(
|
||||||
child: const Text('Add'),
|
onPressed: () => Navigator.pushNamed(
|
||||||
),
|
context,
|
||||||
|
Routes.login,
|
||||||
|
).then((_) => _load()),
|
||||||
|
child: const Text('Log in to add'),
|
||||||
|
)
|
||||||
|
: FilledButton.tonal(
|
||||||
|
onPressed: () => _addToMyDecks(d),
|
||||||
|
child: const Text('Add'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -367,13 +367,23 @@ class _DeckConfigScreenState extends State<DeckConfigScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return PopScope(
|
||||||
appBar: AppBar(
|
canPop: false,
|
||||||
title: const Text('Attempt Settings'),
|
onPopInvokedWithResult: (didPop, result) {
|
||||||
leading: IconButton(
|
if (didPop) return;
|
||||||
icon: const Icon(Icons.arrow_back),
|
_applyAndPersist();
|
||||||
onPressed: () => Navigator.pop(context, _deck),
|
if (mounted) Navigator.pop(context, _deck);
|
||||||
),
|
},
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Attempt Settings'),
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
_applyAndPersist();
|
||||||
|
if (mounted) Navigator.pop(context, _deck);
|
||||||
|
},
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.code),
|
icon: const Icon(Icons.code),
|
||||||
@@ -667,6 +677,7 @@ class _DeckConfigScreenState extends State<DeckConfigScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -683,9 +683,15 @@ class DeckListScreenState extends State<DeckListScreen> {
|
|||||||
valueListenable: ApiAuthService.instance.currentUser,
|
valueListenable: ApiAuthService.instance.currentUser,
|
||||||
builder: (context, user, _) {
|
builder: (context, user, _) {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return FilledButton.tonal(
|
return Padding(
|
||||||
onPressed: () => Navigator.of(context).pushNamed(Routes.login),
|
padding: const EdgeInsets.only(right: 12),
|
||||||
child: const Text('Log in'),
|
child: FilledButton.tonal(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||||
|
),
|
||||||
|
onPressed: () => Navigator.of(context).pushNamed(Routes.login),
|
||||||
|
child: const Text('Log in'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Row(
|
return Row(
|
||||||
@@ -716,10 +722,10 @@ class DeckListScreenState extends State<DeckListScreen> {
|
|||||||
],
|
],
|
||||||
onSelected: (value) async {
|
onSelected: (value) async {
|
||||||
if (value != 'logout') return;
|
if (value != 'logout') return;
|
||||||
final navigator = Navigator.of(context);
|
|
||||||
await ApiAuthService.instance.logout();
|
await ApiAuthService.instance.logout();
|
||||||
navigator.pushNamedAndRemoveUntil(
|
if (!mounted) return;
|
||||||
Routes.login,
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
Routes.deckList,
|
||||||
(route) => false,
|
(route) => false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -61,9 +61,15 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
valueListenable: ApiAuthService.instance.currentUser,
|
valueListenable: ApiAuthService.instance.currentUser,
|
||||||
builder: (context, user, _) {
|
builder: (context, user, _) {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return FilledButton.tonal(
|
return Padding(
|
||||||
onPressed: () => Navigator.of(context).pushNamed(Routes.login),
|
padding: const EdgeInsets.only(right: 12),
|
||||||
child: const Text('Log in'),
|
child: FilledButton.tonal(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||||
|
),
|
||||||
|
onPressed: () => Navigator.of(context).pushNamed(Routes.login),
|
||||||
|
child: const Text('Log in'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Row(
|
return Row(
|
||||||
@@ -94,10 +100,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
],
|
],
|
||||||
onSelected: (value) async {
|
onSelected: (value) async {
|
||||||
if (value != 'logout') return;
|
if (value != 'logout') return;
|
||||||
final navigator = Navigator.of(context);
|
|
||||||
await ApiAuthService.instance.logout();
|
await ApiAuthService.instance.logout();
|
||||||
navigator.pushNamedAndRemoveUntil(
|
if (!mounted) return;
|
||||||
Routes.login,
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
Routes.deckList,
|
||||||
(route) => false,
|
(route) => false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -123,10 +129,12 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
),
|
),
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||||
bottomNavigationBar: BottomAppBar(
|
bottomNavigationBar: BottomAppBar(
|
||||||
notchMargin: 8,
|
notchMargin: 2,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
height: 40,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
_NavItem(
|
_NavItem(
|
||||||
icon: Icons.folder,
|
icon: Icons.folder,
|
||||||
label: 'My Decks',
|
label: 'My Decks',
|
||||||
@@ -166,16 +174,16 @@ class _NavItem extends StatelessWidget {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
customBorder: const CircleBorder(),
|
customBorder: const CircleBorder(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, size: 22, color: selected ? Theme.of(context).colorScheme.primary : null),
|
Icon(icon, size: 18, color: selected ? Theme.of(context).colorScheme.primary : null),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 10,
|
||||||
color: selected ? Theme.of(context).colorScheme.primary : null,
|
color: selected ? Theme.of(context).colorScheme.primary : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../routes.dart';
|
|
||||||
import '../services/api_auth_service.dart';
|
import '../services/api_auth_service.dart';
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
class LoginScreen extends StatefulWidget {
|
||||||
@@ -54,7 +53,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (err == null) {
|
if (err == null) {
|
||||||
Navigator.pushReplacementNamed(context, Routes.deckList);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user