Feat: Passt App für kleinere Bildschirme an
This commit is contained in:
@@ -20,6 +20,7 @@ class Dashboard extends StatelessWidget {
|
|||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -44,5 +45,6 @@ class Dashboard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,12 @@ class DynamicDialog {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Column(
|
content: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxHeight: MediaQuery.of(ctx).size.height * 0.7,
|
||||||
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (content != null) content!,
|
if (content != null) content!,
|
||||||
@@ -123,6 +128,8 @@ class DynamicDialog {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
actions: actions
|
actions: actions
|
||||||
.map(
|
.map(
|
||||||
(final action) => TextButton(
|
(final action) => TextButton(
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class _MonthlyBalanceChart extends State<MonthlyBalanceChart> {
|
|||||||
final AsyncSnapshot<List<Map<String, dynamic>>> snapshot,
|
final AsyncSnapshot<List<Map<String, dynamic>>> snapshot,
|
||||||
) {
|
) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
|
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||||
|
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final List<Map<String, dynamic>> monthlyBalances = snapshot.data!;
|
final List<Map<String, dynamic>> monthlyBalances = snapshot.data!;
|
||||||
@@ -109,7 +110,15 @@ class _MonthlyBalanceChart extends State<MonthlyBalanceChart> {
|
|||||||
final DateTime date = value['date'];
|
final DateTime date = value['date'];
|
||||||
final DateFormat format = DateFormat('MMMM');
|
final DateFormat format = DateFormat('MMMM');
|
||||||
|
|
||||||
return format.format(date);
|
String month = format.format(date);
|
||||||
|
|
||||||
|
if (mediaQuery.size.width < 470) {
|
||||||
|
month = month.substring(0, 1);
|
||||||
|
} else if (mediaQuery.size.width < 920) {
|
||||||
|
month = month.substring(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return month;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
if (value.toInt() >= 0 &&
|
if (value.toInt() >= 0 &&
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:routemaster/routemaster.dart';
|
import 'package:routemaster/routemaster.dart';
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ class _InputFields extends State<InputFields> {
|
|||||||
final TextEditingController _amountMaxController = TextEditingController();
|
final TextEditingController _amountMaxController = TextEditingController();
|
||||||
final TextEditingController _dateTimeController = TextEditingController();
|
final TextEditingController _dateTimeController = TextEditingController();
|
||||||
|
|
||||||
|
static const double _filterBreakpoint = 600;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -83,7 +87,18 @@ class _InputFields extends State<InputFields> {
|
|||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
|
|
||||||
return Row(
|
return LayoutBuilder(
|
||||||
|
builder: (final context, final constraints) {
|
||||||
|
if (constraints.maxWidth < _filterBreakpoint) {
|
||||||
|
return _buildFilterButton(context, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buildInlineFilters(theme);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInlineFilters(final ThemeData theme) => Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@@ -92,9 +107,7 @@ class _InputFields extends State<InputFields> {
|
|||||||
labelText: 'Name',
|
labelText: 'Name',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
onChanged: (final String value) {
|
onChanged: (_) => _updateUrl(),
|
||||||
_updateUrl();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
@@ -106,9 +119,7 @@ class _InputFields extends State<InputFields> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||||
onChanged: (final String value) {
|
onChanged: (_) => _updateUrl(),
|
||||||
_updateUrl();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
@@ -120,18 +131,14 @@ class _InputFields extends State<InputFields> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||||
onChanged: (final String value) {
|
onChanged: (_) => _updateUrl(),
|
||||||
_updateUrl();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DateRangePicker(
|
child: DateRangePicker(
|
||||||
controller: _dateTimeController,
|
controller: _dateTimeController,
|
||||||
onChanged: (final DateTimeRange? value) {
|
onChanged: (_) => _updateUrl(),
|
||||||
_updateUrl();
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Zeitraum',
|
labelText: 'Zeitraum',
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
@@ -143,19 +150,100 @@ class _InputFields extends State<InputFields> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
IconButton(
|
IconButton(onPressed: _clear, icon: const Icon(Icons.clear)),
|
||||||
onPressed: () {
|
|
||||||
_nameController.text = '';
|
|
||||||
_amountMinController.text = '';
|
|
||||||
_amountMaxController.text = '';
|
|
||||||
_dateTimeController.text = '';
|
|
||||||
|
|
||||||
_updateUrl();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildFilterButton(
|
||||||
|
final BuildContext context,
|
||||||
|
final ThemeData theme,
|
||||||
|
) => Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
icon: const Icon(Icons.filter_alt),
|
||||||
|
label: const Text('Filter'),
|
||||||
|
onPressed: () => _openFilterDialog(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
void _openFilterDialog(final BuildContext context) {
|
||||||
|
unawaited(
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (final context) => AlertDialog(
|
||||||
|
title: const Text('Filter'),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _nameController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Name',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
TextField(
|
||||||
|
controller: _amountMinController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Min Betrag €',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
|
decimal: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
TextField(
|
||||||
|
controller: _amountMaxController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Max Betrag €',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
|
decimal: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
DateRangePicker(
|
||||||
|
controller: _dateTimeController,
|
||||||
|
onChanged: (_) {},
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Zeitraum',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_clear();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text('Zurücksetzen'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
_updateUrl();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text('Anwenden'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clear() {
|
||||||
|
_nameController.clear();
|
||||||
|
_amountMinController.clear();
|
||||||
|
_amountMaxController.clear();
|
||||||
|
_dateTimeController.clear();
|
||||||
|
_updateUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateUrl() {
|
void _updateUrl() {
|
||||||
|
|||||||
Reference in New Issue
Block a user