Working on message search+filter

This commit is contained in:
2024-06-16 00:46:46 +02:00
parent e9ea573e33
commit 0bbe5fc7fa
10 changed files with 354 additions and 64 deletions

View File

@@ -1,14 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:simplecloudnotifier/components/layout/app_bar_filter_dialog.dart';
import 'package:simplecloudnotifier/components/layout/app_bar_progress_indicator.dart';
import 'package:simplecloudnotifier/pages/debug/debug_main.dart';
import 'package:simplecloudnotifier/settings/app_settings.dart';
import 'package:simplecloudnotifier/state/app_bar_state.dart';
import 'package:simplecloudnotifier/state/app_theme.dart';
import 'package:simplecloudnotifier/utils/navi.dart';
import 'package:simplecloudnotifier/utils/toaster.dart';
class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
const SCNAppBar({
class SCNAppBar extends StatefulWidget implements PreferredSizeWidget {
SCNAppBar({
Key? key,
required this.title,
required this.showThemeSwitch,
@@ -23,6 +27,22 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
final bool showShare;
final void Function()? onShare;
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
@override
State<SCNAppBar> createState() => _SCNAppBarState();
}
class _SCNAppBarState extends State<SCNAppBar> {
final TextEditingController _ctrlSearchField = TextEditingController();
@override
void dispose() {
_ctrlSearchField.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final cfg = Provider.of<AppSettings>(context);
@@ -39,7 +59,7 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
));
}
if (showThemeSwitch) {
if (widget.showThemeSwitch) {
actions.add(Consumer<AppTheme>(
builder: (context, appTheme, child) => IconButton(
icon: Icon(appTheme.darkMode ? FontAwesomeIcons.solidSun : FontAwesomeIcons.solidMoon),
@@ -48,54 +68,117 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
),
));
} else {
actions.add(Visibility(
visible: false,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: IconButton(
icon: const Icon(FontAwesomeIcons.square),
onPressed: () {/*TODO*/},
),
));
actions.add(_buildSpacer());
}
if (showSearch) {
if (widget.showSearch) {
actions.add(IconButton(
icon: const Icon(FontAwesomeIcons.solidFilter),
tooltip: 'Filter',
onPressed: () => _showFilterDialog(context),
));
actions.add(IconButton(
icon: const Icon(FontAwesomeIcons.solidMagnifyingGlass),
tooltip: 'Search',
onPressed: () {/*TODO*/},
onPressed: () => AppBarState().setShowSearchField(true),
));
} else if (showShare) {
} else if (widget.showShare) {
actions.add(_buildSpacer());
actions.add(IconButton(
icon: const Icon(FontAwesomeIcons.solidShareNodes),
tooltip: 'Share',
onPressed: onShare ?? () {},
onPressed: widget.onShare ?? () {},
));
} else {
actions.add(Visibility(
visible: false,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: IconButton(
icon: const Icon(FontAwesomeIcons.square),
onPressed: () {/*TODO*/},
),
));
actions.add(_buildSpacer());
}
return AppBar(
title: Text(title ?? 'Simple Cloud Notifier 2.0'),
actions: actions,
backgroundColor: Theme.of(context).secondaryHeaderColor,
bottom: PreferredSize(
preferredSize: Size(double.infinity, 1.0),
child: AppBarProgressIndicator(),
return Consumer<AppBarState>(builder: (context, value, child) {
if (value.showSearchField) {
return AppBar(
leading: IconButton(
icon: const Icon(FontAwesomeIcons.solidArrowLeft),
onPressed: () {
value.setShowSearchField(false);
},
),
title: _buildSearchTextField(context),
actions: [
IconButton(
icon: const Icon(FontAwesomeIcons.solidMagnifyingGlass),
onPressed: () {
value.setShowSearchField(false);
AppBarState().notifySearchListeners(_ctrlSearchField.text);
_ctrlSearchField.clear();
},
),
],
backgroundColor: Theme.of(context).secondaryHeaderColor,
bottom: PreferredSize(
preferredSize: Size(double.infinity, 1.0),
child: AppBarProgressIndicator(show: value.loadingIndeterminate),
),
);
} else {
return AppBar(
title: Text(widget.title ?? 'SCN'),
actions: actions,
backgroundColor: Theme.of(context).secondaryHeaderColor,
bottom: PreferredSize(
preferredSize: Size(double.infinity, 1.0),
child: AppBarProgressIndicator(show: value.loadingIndeterminate),
),
);
}
});
}
Visibility _buildSpacer() {
return Visibility(
visible: false,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: IconButton(
icon: const Icon(FontAwesomeIcons.square),
onPressed: () {/* NO-OP */},
),
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
Widget _buildSearchTextField(BuildContext context) {
return TextField(
controller: _ctrlSearchField,
autofocus: true,
style: TextStyle(fontSize: 20),
textInputAction: TextInputAction.search,
decoration: InputDecoration(
hintText: 'Search',
),
onSubmitted: (value) {
AppBarState().setShowSearchField(false);
AppBarState().notifySearchListeners(_ctrlSearchField.text);
_ctrlSearchField.clear();
},
);
}
void _showFilterDialog(BuildContext context) {
double vpWidth = MediaQuery.sizeOf(context).width;
showDialog<void>(
context: context,
barrierDismissible: true,
barrierColor: Colors.transparent,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
alignment: Alignment.topCenter,
insetPadding: EdgeInsets.fromLTRB(0, this.widget.preferredSize.height, 0, 0),
backgroundColor: Colors.transparent,
child: AppBarFilterDialog(),
);
},
);
}
}

View File

@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:simplecloudnotifier/utils/navi.dart';
class AppBarFilterDialog extends StatefulWidget {
@override
_AppBarFilterDialogState createState() => _AppBarFilterDialogState();
}
class _AppBarFilterDialogState extends State<AppBarFilterDialog> {
double _height = 0;
double _targetHeight = 4 + (48 * 6) + (16 * 5) + 4;
@override
void initState() {
super.initState();
Future.delayed(Duration.zero, () {
setState(() {
_height = _targetHeight;
});
});
}
@override
Widget build(BuildContext context) {
double vpWidth = MediaQuery.sizeOf(context).width;
return Container(
margin: const EdgeInsets.all(0),
width: vpWidth,
color: Colors.transparent,
child: Column(
children: [
Container(
color: Theme.of(context).secondaryHeaderColor,
child: AnimatedContainer(
duration: Duration(milliseconds: 350),
curve: Curves.easeInCubic,
height: _height,
child: ClipRect(
child: OverflowBox(
alignment: Alignment.topCenter,
maxWidth: vpWidth,
minWidth: vpWidth,
minHeight: 0,
maxHeight: _targetHeight,
child: Column(
children: [
SizedBox(height: 4),
_buildFilterItem(context, FontAwesomeIcons.magnifyingGlass, 'Search'),
Divider(),
_buildFilterItem(context, FontAwesomeIcons.snake, 'Channel'),
Divider(),
_buildFilterItem(context, FontAwesomeIcons.signature, 'Sender'),
Divider(),
_buildFilterItem(context, FontAwesomeIcons.timer, 'Time'),
Divider(),
_buildFilterItem(context, FontAwesomeIcons.bolt, 'Priority'),
Divider(),
_buildFilterItem(context, FontAwesomeIcons.gearCode, 'Key'),
SizedBox(height: 4),
],
),
),
),
),
),
Expanded(child: GestureDetector(child: Container(width: vpWidth, color: Color(0x88000000)), onTap: () => Navi.popDialog(context))),
],
),
);
}
Widget _buildFilterItem(BuildContext context, IconData icon, String label) {
return ListTile(
visualDensity: VisualDensity.compact,
title: Text(label),
leading: Icon(icon),
onTap: () {
Navi.popDialog(context);
//TOOD show more...
},
);
}
}

View File

@@ -1,21 +1,19 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:simplecloudnotifier/state/app_bar_state.dart';
class AppBarProgressIndicator extends StatelessWidget implements PreferredSizeWidget {
AppBarProgressIndicator({required this.show});
final bool show;
@override
Size get preferredSize => Size(double.infinity, 1.0);
@override
Widget build(BuildContext context) {
return Consumer<AppBarState>(
builder: (context, value, child) {
if (value.loadingIndeterminate) {
return LinearProgressIndicator(value: null);
} else {
return SizedBox.square(dimension: 4); // 4 height is the same as the LinearProgressIndicator
}
},
);
if (show) {
return LinearProgressIndicator(value: null);
} else {
return SizedBox.square(dimension: 4); // 4 height is the same as the LinearProgressIndicator
}
}
}