add websock functionality

This commit is contained in:
Alexander Laevens
2022-11-30 02:52:56 -07:00
parent 34edcd53cb
commit 339b0c6ad9
41 changed files with 1028 additions and 189 deletions

View File

@@ -0,0 +1,85 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:one_trip/api/models/listingredient.dart';
class ListRow extends StatefulWidget {
final ListIngredient ingredient;
final Future<bool> Function(ListIngredient ingredient) apiRemove;
final Function(bool value) onToggle;
final int index;
const ListRow({
super.key,
required this.ingredient,
required this.onToggle,
required this.index,
required this.apiRemove,
});
@override
State<ListRow> createState() => _ListRowState();
}
class _ListRowState extends State<ListRow> {
double dismissAmount = 0.0;
bool willDismiss = false;
final UniqueKey key = UniqueKey();
@override
void didUpdateWidget(covariant ListRow oldWidget) {
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => widget.onToggle(!widget.ingredient.inCart),
child: Dismissible(
key: key,
direction: DismissDirection.endToStart,
onUpdate: (details) => setState(() {
dismissAmount = details.progress;
willDismiss = details.reached;
}),
confirmDismiss: (direction) async =>
await widget.apiRemove(widget.ingredient),
background: Container(
color: Color.lerp(
Colors.transparent, Colors.red, min(dismissAmount * 2.5, 1)),
child: Align(
alignment: Alignment.centerRight,
child: SizedBox(
width: 45,
child: Icon(
Icons.delete,
size: min(27.5 * dismissAmount + 20, 35),
color: Colors.white,
),
),
),
),
child: Row(
children: [
Checkbox(
value: widget.ingredient.inCart,
onChanged: (value) => widget.onToggle(value!),
),
Expanded(
child: Text(
// _ingredient.name,
widget.ingredient.name,
style: Theme.of(context).textTheme.titleMedium!.copyWith(
decoration: widget.ingredient.inCart
? TextDecoration.lineThrough
: null,
color: widget.ingredient.inCart ? Colors.green : null,
),
),
),
// IconButton(onPressed: () {}, icon: const Icon(Icons.edit)),
],
),
),
);
}
}

View File

@@ -0,0 +1,168 @@
import 'package:flutter/material.dart';
import 'package:one_trip/api/models/recipe.dart';
import 'package:one_trip/api/searchresult.dart';
import 'package:one_trip/theme.dart';
import 'package:one_trip/widgets/pagination_listview.dart';
class SearchRecipesDialog extends StatefulWidget {
const SearchRecipesDialog({super.key});
@override
State<SearchRecipesDialog> createState() => _SearchRecipesDialogState();
}
class _SearchRecipesDialogState extends State<SearchRecipesDialog> {
final TextEditingController _searchController = TextEditingController();
ListViewState _listState = ListViewState.inactive;
List<int> selectedIDs = [];
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Search your Recipes",
style: Theme.of(context).textTheme.titleMedium,
),
const Divider(),
TextFormField(
controller: _searchController,
textInputAction: TextInputAction.search,
onFieldSubmitted: (value) {
setState(() {
_listState = ListViewState.changed;
});
},
onChanged: (value) {
setState(() {
_listState = ListViewState.inactive;
});
},
decoration: InputDecoration(
label: const Text("Search"),
isDense: true,
suffix: IconButton(
onPressed: () {
setState(() {
_listState = ListViewState.changed;
});
// https://flutterigniter.com/dismiss-keyboard-form-lose-focus/
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
icon: const Icon(Icons.search),
),
),
),
const SizedBox(height: 8),
LayoutBuilder(
builder: (builder, constraints) {
return ConstrainedBox(
constraints: BoxConstraints.expand(
width: constraints.maxWidth - 8,
height: 160,
),
child: PaginationListView(
state: _listState,
shrinkWrap: true,
itemBuilder: (context, data) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_listState = ListViewState.inUse;
if (selectedIDs.contains(data.id)) {
selectedIDs.remove(data.id);
} else {
selectedIDs.add(data.id);
}
});
},
child: Container(
padding: const EdgeInsets.all(4),
color: selectedIDs.contains(data.id)
? Theme.of(context).colorScheme.secondary
: null,
child: Text(data.name),
),
);
},
seperatorBuilder: (context, data) {
return const Divider();
},
dataProvider: (int page) async {
// SearchResult<SimpleUser> result =
// await SimpleUser.search(_searchController.text, page);
// List<dynamic> users = List<dynamic>.from(result.results);
// if (result.next == null) {
// users.add(null);
// }
// return users;
SearchResult<Recipe> result =
await Recipe.search(_searchController.text, page);
List<dynamic> recipes =
List<dynamic>.from(result.results);
if (result.next == null) {
recipes.add(null);
}
return recipes;
},
),
);
},
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, selectedIDs),
child: const Text("Done")),
],
),
)
],
),
),
);
}
}
Future<List<int>?> searchRecipesDialog(BuildContext context) async {
List<int>? selectedIDs = await showDialog(
context: context,
builder: (context) {
return Dialog(
child: ScrollConfiguration(
behavior: MyBehavior(), child: const SearchRecipesDialog()),
);
},
);
return selectedIDs;
}