workouttest_app/lib/widgets/menu_search_bar.dart
2021-04-19 00:16:07 +02:00

283 lines
8.6 KiB
Dart

import 'dart:async';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:aitrainer_app/library/dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class SearchBarStream {
static final SearchBarStream _singleton = SearchBarStream._internal();
final StreamController<bool> streamController = StreamController<bool>.broadcast();
bool searching = false;
Stream get stream => streamController.stream;
StreamController getStreamController() => streamController;
factory SearchBarStream() => _singleton;
SearchBarStream._internal();
void dispose() {
streamController.close();
}
}
// ignore: must_be_immutable
class MenuSearchBar extends StatelessWidget {
final List<WorkoutMenuTree> listItems;
final Function(WorkoutMenuTree) onFind;
bool showIcon;
MenuSearchBar({required this.listItems, required this.onFind, this.showIcon = true});
@override
Widget build(BuildContext context) {
return AnimatedSearch(
listItems: listItems,
onFind: onFind,
showIcon: showIcon,
);
}
}
// ignore: must_be_immutable
class AnimatedSearch extends StatefulWidget {
final List<WorkoutMenuTree> listItems;
final Function(WorkoutMenuTree) onFind;
bool showIcon = true;
AnimatedSearch({required this.listItems, required this.onFind, required this.showIcon});
@override
_AnimatedSearch createState() => _AnimatedSearch();
}
class _AnimatedSearch extends State<AnimatedSearch> {
bool isSearching = false;
final Stream stream = SearchBarStream().stream;
var subscription;
@override
void initState() {
super.initState();
}
@override
void didChangeDependencies() {
subscription = stream.listen((value) {
setState(() {
isSearching = SearchBarStream().searching;
});
});
super.didChangeDependencies();
}
@override
void dispose() {
subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 0),
child: Stack(
alignment: Alignment.center,
children: [
AnimateExpansion(
animate: widget.showIcon ? !isSearching : false,
axisAlignment: 1.0,
child: IconButton(
onPressed: () => {
setState(() {
isSearching = !isSearching;
SearchBarStream().searching = isSearching;
SearchBarStream().getStreamController().add(true);
})
},
icon: Icon(
Icons.search,
color: Color(0xffb4f500),
size: 40,
),
)),
AnimateExpansion(
animate: widget.showIcon ? isSearching : true,
axisAlignment: -1.0,
child: Search(
listItems: widget.listItems,
onFind: widget.onFind,
),
),
],
));
}
}
// ignore: must_be_immutable
class Search extends StatelessWidget with Trans {
final List<WorkoutMenuTree> listItems;
final Function(WorkoutMenuTree) onFind;
Search({required this.listItems, required this.onFind});
@override
Widget build(BuildContext context) {
setContext(context);
return SizedBox(
width: MediaQuery.of(context).size.width * .6,
child: DropdownSearch<WorkoutMenuTree>(
onPopupDismissed: () => {SearchBarStream().searching = false, SearchBarStream().getStreamController().add(false)},
showAsSuffixIcons: false,
showSearchBox: true,
mode: Mode.DIALOG,
showSelectedItem: false,
items: listItems,
onChanged: (value) => onFind(value),
dropdownSearchDecoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 15, top: 0, bottom: 5),
labelStyle: GoogleFonts.inter(fontSize: 12, color: Colors.yellow[50]),
fillColor: Colors.black38,
filled: true,
border: OutlineInputBorder(
gapPadding: 8.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
searchBoxController: TextEditingController(),
searchBoxDecoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 15, top: 5, bottom: 5),
labelText: t("Search Exercises..."),
labelStyle: GoogleFonts.inter(fontSize: 12, color: Colors.grey[400]),
fillColor: Colors.white24,
filled: true,
border: OutlineInputBorder(
gapPadding: 8.0,
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: Colors.white12, width: 0.4),
),
),
popupBackgroundColor: Colors.grey[700],
popupItemBuilder: (context, item, isSelected) {
return Container(
margin: EdgeInsets.all(3),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('asset/image/WT_black_G_background.jpg'),
fit: BoxFit.cover,
),
),
child: ListTile(
selected: isSelected,
title: Text(
item.name,
style: GoogleFonts.inter(color: Colors.yellow[300], fontSize: 16),
),
subtitle: Text(
item.parentName,
maxLines: 1,
style: GoogleFonts.inter(color: Colors.grey[400]),
),
leading: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.asset(item.imageName),
),
),
);
},
emptyBuilder: (context, searchEntry) => Center(
child: Text(
t("No exercise found"),
textAlign: TextAlign.center,
style: GoogleFonts.inter(color: Colors.yellow[200], fontSize: 16),
)),
dropdownBuilder: (context, WorkoutMenuTree? item, itemDesignation) => Container(
child: ListView(scrollDirection: Axis.vertical, shrinkWrap: true, children: [
(item == null)
? Container(
height: 15,
padding: EdgeInsets.all(0),
child: Text(
t("Search Exercises..."),
style: GoogleFonts.inter(color: Colors.grey[400], fontSize: 14),
),
)
: Container(
height: 15,
padding: EdgeInsets.all(0),
child: Text(
item.name,
maxLines: 3,
style: GoogleFonts.inter(color: Colors.yellow[400], fontSize: 14),
),
),
]))),
);
}
}
class AnimateExpansion extends StatefulWidget {
final Widget child;
final bool animate;
final double axisAlignment;
AnimateExpansion({
this.animate = false,
required this.axisAlignment,
required this.child,
});
@override
_AnimateExpansionState createState() => _AnimateExpansionState();
}
class _AnimateExpansionState extends State<AnimateExpansion> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
void prepareAnimations() {
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 350),
);
_animation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeInCubic,
reverseCurve: Curves.easeOutCubic,
);
}
void _toggle() {
if (widget.animate) {
_animationController.forward();
} else {
_animationController.reverse();
}
}
@override
void initState() {
super.initState();
prepareAnimations();
_toggle();
}
@override
void didUpdateWidget(AnimateExpansion oldWidget) {
super.didUpdateWidget(oldWidget);
_toggle();
}
@override
Widget build(BuildContext context) {
return SizeTransition(axis: Axis.horizontal, axisAlignment: -1.0, sizeFactor: _animation, child: widget.child);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}