CreateGroupView erstellt #28
@@ -3,10 +3,15 @@ import 'package:game_tracker/core/custom_theme.dart';
|
|||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
import 'package:game_tracker/data/dto/group.dart';
|
import 'package:game_tracker/data/dto/group.dart';
|
||||||
import 'package:game_tracker/data/dto/player.dart';
|
import 'package:game_tracker/data/dto/player.dart';
|
||||||
|
import 'package:game_tracker/presentation/widgets/custom_search_bar.dart';
|
||||||
import 'package:game_tracker/presentation/widgets/full_width_button.dart';
|
import 'package:game_tracker/presentation/widgets/full_width_button.dart';
|
||||||
|
import 'package:game_tracker/presentation/widgets/text_input_field.dart';
|
||||||
|
import 'package:game_tracker/presentation/widgets/tiles/text_icon_list_tile.dart';
|
||||||
|
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
|
||||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class CreateGroupView extends StatefulWidget {
|
class CreateGroupView extends StatefulWidget {
|
||||||
const CreateGroupView({super.key});
|
const CreateGroupView({super.key});
|
||||||
@@ -61,29 +66,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
child: TextField(
|
child: TextInputField(
|
||||||
controller: _groupNameController,
|
controller: _groupNameController,
|
||||||
|
hintText: 'Group name',
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
decoration: InputDecoration(
|
|
||||||
filled: true,
|
|
||||||
fillColor: CustomTheme.boxColor,
|
|
||||||
hint: Text(
|
|
||||||
"Group name",
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
|
||||||
borderSide: BorderSide(color: CustomTheme.boxBorder),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
|
||||||
borderSide: BorderSide(color: CustomTheme.boxBorder),
|
|
||||||
),
|
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -104,29 +92,16 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SearchBar(
|
CustomSearchBar(
|
||||||
controller: _searchBarController,
|
controller: _searchBarController,
|
||||||
constraints: BoxConstraints(maxHeight: 45, minHeight: 45),
|
constraints: BoxConstraints(maxHeight: 45, minHeight: 45),
|
||||||
|
sneeex marked this conversation as resolved
Outdated
|
|||||||
hintText: "Search for players",
|
hintText: "Search for players",
|
||||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
single quotes single quotes
|
|||||||
hintStyle: WidgetStateProperty.all(
|
|
||||||
TextStyle(fontSize: 16),
|
|
||||||
),
|
|
||||||
leading: Icon(Icons.search),
|
|
||||||
backgroundColor: WidgetStateProperty.all(
|
|
||||||
CustomTheme.boxColor,
|
|
||||||
),
|
|
||||||
side: WidgetStateProperty.all(
|
|
||||||
BorderSide(color: CustomTheme.boxBorder),
|
|
||||||
),
|
|
||||||
shape: WidgetStateProperty.all(
|
|
||||||
RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
suggestedPlayers = [...allPlayers];
|
suggestedPlayers = allPlayers.where((player) {
|
||||||
|
return !selectedPlayers.contains(player);
|
||||||
|
}).toList();
|
||||||
} else {
|
} else {
|
||||||
suggestedPlayers = allPlayers.where((player) {
|
suggestedPlayers = allPlayers.where((player) {
|
||||||
final bool nameMatches = player.name
|
final bool nameMatches = player.name
|
||||||
@@ -156,50 +131,25 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
for (var selectedPlayer in selectedPlayers)
|
for (var selectedPlayer in selectedPlayers)
|
||||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
lieber lieber `var player in selectedPlayers`?
|
|||||||
Container(
|
TextIconTile(
|
||||||
padding: EdgeInsets.all(5),
|
text: selectedPlayer.name,
|
||||||
decoration: BoxDecoration(
|
icon: Icons.close,
|
||||||
color: CustomTheme.onBoxColor,
|
onIconTap: () {
|
||||||
borderRadius: BorderRadius.circular(12),
|
setState(() {
|
||||||
),
|
final currentSearch = _searchBarController.text
|
||||||
child: Row(
|
.toLowerCase();
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
selectedPlayers.remove(selectedPlayer);
|
||||||
mainAxisSize: MainAxisSize.min,
|
if (currentSearch.isEmpty ||
|
||||||
children: [
|
selectedPlayer.name.toLowerCase().contains(
|
||||||
SizedBox(width: 12),
|
currentSearch,
|
||||||
Flexible(
|
)) {
|
||||||
child: Text(
|
suggestedPlayers.add(selectedPlayer);
|
||||||
selectedPlayer.name,
|
suggestedPlayers.sort(
|
||||||
overflow: TextOverflow.ellipsis,
|
(a, b) => a.name.compareTo(b.name),
|
||||||
style: const TextStyle(
|
);
|
||||||
fontSize: 14,
|
}
|
||||||
fontWeight: FontWeight.w500,
|
});
|
||||||
),
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 3),
|
|
||||||
GestureDetector(
|
|
||||||
child: const Icon(Icons.close, size: 20),
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
final currentSearch = _searchBarController
|
|
||||||
.text
|
|
||||||
.toLowerCase();
|
|
||||||
selectedPlayers.remove(selectedPlayer);
|
|
||||||
if (currentSearch.isEmpty ||
|
|
||||||
selectedPlayer.name
|
|
||||||
.toLowerCase()
|
|
||||||
.contains(currentSearch)) {
|
|
||||||
suggestedPlayers.add(selectedPlayer);
|
|
||||||
suggestedPlayers.sort(
|
|
||||||
(a, b) => a.name.compareTo(b.name),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -214,125 +164,100 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
`const SizedBox()`
|
|||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _allPlayersFuture,
|
future: _allPlayersFuture,
|
||||||
builder: (BuildContext context, AsyncSnapshot<List<Player>> snapshot) {
|
builder:
|
||||||
if (snapshot.hasError) {
|
(
|
||||||
return const Center(
|
BuildContext context,
|
||||||
child: TopCenteredMessage(
|
AsyncSnapshot<List<Player>> snapshot,
|
||||||
icon: Icons.report,
|
) {
|
||||||
title: 'Error',
|
if (snapshot.hasError) {
|
||||||
message: 'Player data couldn\'t\nbe loaded.',
|
return const Center(
|
||||||
),
|
child: TopCenteredMessage(
|
||||||
);
|
icon: Icons.report,
|
||||||
}
|
title: 'Error',
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
message: 'Player data couldn\'t\nbe loaded.',
|
||||||
(!snapshot.hasData ||
|
),
|
||||||
snapshot.data!.isEmpty ||
|
);
|
||||||
(selectedPlayers.isEmpty &&
|
}
|
||||||
allPlayers.isEmpty))) {
|
if (snapshot.connectionState ==
|
||||||
return const Center(
|
ConnectionState.done &&
|
||||||
child: TopCenteredMessage(
|
(!snapshot.hasData ||
|
||||||
icon: Icons.info,
|
snapshot.data!.isEmpty ||
|
||||||
title: 'Info',
|
(selectedPlayers.isEmpty &&
|
||||||
message: 'No players created yet.',
|
allPlayers.isEmpty))) {
|
||||||
),
|
return const Center(
|
||||||
);
|
child: TopCenteredMessage(
|
||||||
}
|
icon: Icons.info,
|
||||||
final bool isLoading =
|
title: 'Info',
|
||||||
snapshot.connectionState == ConnectionState.waiting;
|
message: 'No players created yet.',
|
||||||
return Expanded(
|
),
|
||||||
child: Skeletonizer(
|
);
|
||||||
effect: PulseEffect(
|
}
|
||||||
from: Colors.grey[800]!,
|
final bool isLoading =
|
||||||
to: Colors.grey[600]!,
|
snapshot.connectionState ==
|
||||||
duration: const Duration(milliseconds: 800),
|
ConnectionState.waiting;
|
||||||
),
|
return Expanded(
|
||||||
enabled: isLoading,
|
child: Skeletonizer(
|
||||||
enableSwitchAnimation: true,
|
effect: PulseEffect(
|
||||||
switchAnimationConfig: const SwitchAnimationConfig(
|
from: Colors.grey[800]!,
|
||||||
duration: Duration(milliseconds: 200),
|
to: Colors.grey[600]!,
|
||||||
switchInCurve: Curves.linear,
|
duration: const Duration(milliseconds: 800),
|
||||||
switchOutCurve: Curves.linear,
|
),
|
||||||
transitionBuilder:
|
enabled: isLoading,
|
||||||
AnimatedSwitcher.defaultTransitionBuilder,
|
enableSwitchAnimation: true,
|
||||||
layoutBuilder:
|
switchAnimationConfig:
|
||||||
AnimatedSwitcher.defaultLayoutBuilder,
|
const SwitchAnimationConfig(
|
||||||
),
|
duration: Duration(milliseconds: 200),
|
||||||
child:
|
switchInCurve: Curves.linear,
|
||||||
(suggestedPlayers.isEmpty &&
|
switchOutCurve: Curves.linear,
|
||||||
!allPlayers.isEmpty)
|
transitionBuilder: AnimatedSwitcher
|
||||||
? TopCenteredMessage(
|
.defaultTransitionBuilder,
|
||||||
icon: Icons.info,
|
layoutBuilder:
|
||||||
title: 'Info',
|
AnimatedSwitcher.defaultLayoutBuilder,
|
||||||
message:
|
),
|
||||||
(selectedPlayers.length ==
|
child:
|
||||||
allPlayers.length)
|
(suggestedPlayers.isEmpty &&
|
||||||
? 'No more players to add.'
|
!allPlayers.isEmpty)
|
||||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Lieber Lieber `isNotEmpty`
|
|||||||
: 'No players found with that name.',
|
? TopCenteredMessage(
|
||||||
)
|
icon: Icons.info,
|
||||||
: ListView.builder(
|
title: 'Info',
|
||||||
itemCount: suggestedPlayers.length,
|
message:
|
||||||
itemBuilder: (BuildContext context, int index) {
|
(selectedPlayers.length ==
|
||||||
return Container(
|
allPlayers.length)
|
||||||
margin: const EdgeInsets.symmetric(
|
? 'No more players to add.'
|
||||||
horizontal: 5,
|
: 'No players found with that name.',
|
||||||
vertical: 5,
|
)
|
||||||
),
|
: ListView.builder(
|
||||||
padding: const EdgeInsets.symmetric(
|
itemCount: suggestedPlayers.length,
|
||||||
horizontal: 10,
|
itemBuilder:
|
||||||
),
|
(BuildContext context, int index) {
|
||||||
decoration: BoxDecoration(
|
return IconListTile(
|
||||||
color: CustomTheme.boxColor,
|
text: suggestedPlayers[index]
|
||||||
border: Border.all(
|
.name,
|
||||||
color: CustomTheme.boxBorder,
|
icon: Icons.add,
|
||||||
),
|
onPressed: () {
|
||||||
borderRadius: BorderRadius.circular(
|
setState(() {
|
||||||
12,
|
if (!selectedPlayers.contains(
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
suggestedPlayers[index].name,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.add, size: 20),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
if (!selectedPlayers.contains(
|
|
||||||
suggestedPlayers[index],
|
|
||||||
)) {
|
|
||||||
selectedPlayers.add(
|
|
||||||
suggestedPlayers[index],
|
suggestedPlayers[index],
|
||||||
);
|
)) {
|
||||||
selectedPlayers.sort(
|
selectedPlayers.add(
|
||||||
(a, b) => a.name
|
suggestedPlayers[index],
|
||||||
.compareTo(b.name),
|
);
|
||||||
);
|
selectedPlayers.sort(
|
||||||
suggestedPlayers.remove(
|
(a, b) => a.name
|
||||||
suggestedPlayers[index],
|
.compareTo(b.name),
|
||||||
);
|
);
|
||||||
}
|
suggestedPlayers.remove(
|
||||||
});
|
suggestedPlayers[index],
|
||||||
},
|
);
|
||||||
),
|
}
|
||||||
],
|
});
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -348,11 +273,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
(_groupNameController.text.isEmpty || selectedPlayers.isEmpty)
|
(_groupNameController.text.isEmpty || selectedPlayers.isEmpty)
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
String id = "ID_" + _groupNameController.text;
|
|
||||||
String name = _groupNameController.text;
|
|
||||||
List<Player> members = selectedPlayers;
|
|
||||||
bool success = await db.groupDao.addGroup(
|
bool success = await db.groupDao.addGroup(
|
||||||
group: Group(id: id, name: name, members: members),
|
group: Group(
|
||||||
|
id: Uuid().v4(),
|
||||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
`const` bzw #35 reinmergen und weglassen
|
|||||||
|
name: _groupNameController.text,
|
||||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Füg hier folgende Zeile ein und entferne die anderen beiden Füg hier folgende Zeile ein und entferne die anderen beiden `if`s
```dart
if (!context.mounted) return;
```
sneeex
commented
Aber wenn's nicht mounted ist, will ich doch trotzdem noch die Felder clearen? > Füg hier folgende Zeile ein und entferne die anderen beiden `if`s
> ```dart
> if (!context.mounted) return;
> ```
Aber wenn's nicht mounted ist, will ich doch trotzdem noch die Felder clearen?
flixcoo
commented
Der Context ist nicht mehr verfügbar, wenn z.B. das Widget garnicht mehr im widget tree ist. Also ist das dann eh egal Der Context ist nicht mehr verfügbar, wenn z.B. das Widget garnicht mehr im widget tree ist. Also ist das dann eh egal
|
|||||||
|
members: selectedPlayers,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
_groupNameController.clear();
|
_groupNameController.clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user
constraints: const BoxConstraints(...