import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:kulinar_app/constants.dart'; import 'package:kulinar_app/widgets/error_widgets.dart'; import 'package:kulinar_app/widgets/toastbar_widget.dart'; import 'package:kulinar_app/widgets/custom_drawer_widget.dart'; import 'package:kulinar_app/models/data/shoplist_data_class.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class ShoplistView extends StatefulWidget { const ShoplistView({Key? key}) : super(key: key); @override ShoplistViewState createState() => ShoplistViewState(); } class ShoplistViewState extends State { final FocusNode _focusNode = FocusNode(); final TextEditingController _controller = TextEditingController(); final GlobalKey _scaffoldKey = GlobalKey(); final GlobalKey _listKey = GlobalKey(); PersistentBottomSheetController? _bottomSheetController; bool _open = false; @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, appBar: AppBar(title: Text(AppLocalizations.of(context)!.category7)), drawer: const CustomDrawer(initialIndex: 4), floatingActionButton: _buildFloatingActionButton(), body: _buildAnimatedList(), ); } Widget _buildFloatingActionButton() { return FloatingActionButton( child: Icon(_open ? Icons.remove_rounded : Icons.add_rounded, color: cIconColor), onPressed: () { if (_open) { _closeInput(); } else { _bottomSheetController = _scaffoldKey.currentState!.showBottomSheet((context) => _buildBottomSheetInput()); _open = !_open; setState(() {}); } }, ); } // TODO: Sometimes exception "cant call insertItem of null" is thrown when first item is added or last is removed Widget _buildAnimatedList() { if (ShoplistData.shoplist.isEmpty) return const NoContentError(); return AnimatedList( key: _listKey, initialItemCount: ShoplistData.shoplist.length, itemBuilder: (BuildContext context, int index, Animation animation) { return SlideTransition( position: animation.drive(Tween(begin: const Offset(1.0, 0.0), end: const Offset(0.0, 0.0))), child: Dismissible( key: Key(_getUniqueKeyString(ShoplistData.shoplist[index])), background: _buildDimissibleBackground(true), secondaryBackground: _buildDimissibleBackground(false), child: ListTile( title: Text(ShoplistData.shoplist[index]), ), onDismissed: (_) { String _content = AppLocalizations.of(context)!.removed; String _actionLabel = AppLocalizations.of(context)!.undo; _removeItem(index); ToastBar.showToastBar(context, _content, actionLabel: _actionLabel, actionCallback: () => _addItem(ShoplistData.removed.removeLast())); }, ), ); }, ); } Widget _buildDimissibleBackground(bool left) { return Container( color: cPrimaryColor, /* child: Row( mainAxisAlignment: left ? MainAxisAlignment.start : MainAxisAlignment.end, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Icon( Icons.delete_rounded, color: cIconColor, ), ), ], ), */ ); } Widget _buildBottomSheetInput() { _focusNode.requestFocus(); return WillPopScope( child: Container( height: 75.0, color: cPrimaryColor, child: Padding( padding: const EdgeInsets.all(15.0), child: TextFormField( controller: _controller, style: cRecipeTitleStyle.copyWith(color: cIconColor), cursorColor: cIconColor, focusNode: _focusNode, textInputAction: TextInputAction.done, onEditingComplete: () { String _text = _controller.text.trim(); _closeInput(); _controller.clear(); if (_text != "") _addItem(_text); }, ), ), ), onWillPop: () async { return false; }, ); } String _getUniqueKeyString(String item) { return base64Encode(utf8.encode(item)); } void _removeItem(int index) { _listKey.currentState!.removeItem(index, (_, __) => Container()); ShoplistData.removed.add(ShoplistData.shoplist.removeAt(index)); ShoplistData.save(); setState(() {}); } void _addItem(String string) { ShoplistData.shoplist.add(string); if (ShoplistData.shoplist.length > 1) _listKey.currentState!.insertItem(ShoplistData.shoplist.indexOf(string)); ShoplistData.save(); setState(() {}); } void _closeInput() { _bottomSheetController!.close(); _focusNode.unfocus(); _open = !_open; setState(() {}); } }