flutter fixes and pipeline speedup
This commit is contained in:
@@ -76,8 +76,9 @@ class _DashboardState extends State<Dashboard> {
|
||||
if (isInitialLoading)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.surface.withOpacity(0.7),
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surface.withOpacity(0.7),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -183,7 +184,8 @@ class _DashboardState extends State<Dashboard> {
|
||||
_buildCard(
|
||||
context,
|
||||
title: 'On this day',
|
||||
action: data.onThisDay
|
||||
action:
|
||||
data.onThisDay
|
||||
.where((leg) => leg.beginTime.year != DateTime.now().year)
|
||||
.length >
|
||||
5
|
||||
@@ -328,10 +330,10 @@ class _DashboardState extends State<Dashboard> {
|
||||
}
|
||||
|
||||
Widget _buildTripsCard(BuildContext context, DataService data) {
|
||||
final trips_unsorted = data.trips;
|
||||
final tripsUnsorted = data.trips;
|
||||
List trips = [];
|
||||
if (trips_unsorted.isNotEmpty) {
|
||||
trips = [...trips_unsorted]..sort((a, b) => b.tripId.compareTo(a.tripId));
|
||||
if (tripsUnsorted.isNotEmpty) {
|
||||
trips = [...tripsUnsorted]..sort((a, b) => b.tripId.compareTo(a.tripId));
|
||||
}
|
||||
return _buildCard(
|
||||
context,
|
||||
|
||||
@@ -86,8 +86,8 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
final tripIds = sorted.map((t) => t.tripId).toSet();
|
||||
final selectedValue =
|
||||
(_selectedTripId != null && tripIds.contains(_selectedTripId))
|
||||
? _selectedTripId
|
||||
: null;
|
||||
? _selectedTripId
|
||||
: null;
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@@ -100,8 +100,10 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
items: [
|
||||
const DropdownMenuItem(value: null, child: Text('No trip')),
|
||||
...sorted.map(
|
||||
(t) =>
|
||||
DropdownMenuItem<int?>(value: t.tripId, child: Text(t.tripName)),
|
||||
(t) => DropdownMenuItem<int?>(
|
||||
value: t.tripId,
|
||||
child: Text(t.tripName),
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: (val) {
|
||||
@@ -304,7 +306,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
|
||||
if (_useManualMileage) {
|
||||
final body = {
|
||||
"leg_trip": _selectedTripId ?? null,
|
||||
"leg_trip": _selectedTripId,
|
||||
"leg_start": startVal,
|
||||
"leg_end": endVal,
|
||||
"leg_begin_time": _legDateTime.toIso8601String(),
|
||||
@@ -318,7 +320,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
await api.post('/add/manual', body);
|
||||
} else {
|
||||
final body = {
|
||||
"leg_trip": _selectedTripId ?? null,
|
||||
"leg_trip": _selectedTripId,
|
||||
"leg_begin_time": _legDateTime.toIso8601String(),
|
||||
"leg_route": routeStations,
|
||||
"leg_notes": _notesController.text.trim(),
|
||||
@@ -429,14 +431,15 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
_useManualMileage = data['useManualMileage'] ?? _useManualMileage;
|
||||
_selectedTripId = data['selectedTripId'];
|
||||
if (data['routeResult'] is Map<String, dynamic>) {
|
||||
_routeResult =
|
||||
RouteResult.fromJson(Map<String, dynamic>.from(data['routeResult']));
|
||||
_routeResult = RouteResult.fromJson(
|
||||
Map<String, dynamic>.from(data['routeResult']),
|
||||
);
|
||||
_mileageController.text = _routeResult!.distance.toStringAsFixed(2);
|
||||
}
|
||||
if (data['tractionItems'] is List) {
|
||||
_restoreTractionItems(List<Map<String, dynamic>>.from(
|
||||
data['tractionItems'].cast<Map>(),
|
||||
));
|
||||
_restoreTractionItems(
|
||||
List<Map<String, dynamic>>.from(data['tractionItems'].cast<Map>()),
|
||||
);
|
||||
}
|
||||
});
|
||||
_startController.text = data['start'] ?? '';
|
||||
@@ -609,34 +612,34 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
final mileagePanel = _section(
|
||||
'Mileage',
|
||||
[
|
||||
if (_useManualMileage)
|
||||
TextFormField(
|
||||
controller: _mileageController,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
if (_useManualMileage)
|
||||
TextFormField(
|
||||
controller: _mileageController,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Mileage (mi)',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
)
|
||||
else if (_routeResult != null)
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Calculated mileage'),
|
||||
subtitle: Text(
|
||||
'${_routeResult!.distance.toStringAsFixed(2)} mi',
|
||||
),
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Mileage (mi)',
|
||||
border: OutlineInputBorder(),
|
||||
if (!_useManualMileage)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _openCalculator,
|
||||
icon: const Icon(Icons.calculate),
|
||||
label: const Text('Open mileage calculator'),
|
||||
),
|
||||
),
|
||||
)
|
||||
else if (_routeResult != null)
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Calculated mileage'),
|
||||
subtitle: Text(
|
||||
'${_routeResult!.distance.toStringAsFixed(2)} mi',
|
||||
),
|
||||
),
|
||||
if (!_useManualMileage)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _openCalculator,
|
||||
icon: const Icon(Icons.calculate),
|
||||
label: const Text('Open mileage calculator'),
|
||||
),
|
||||
),
|
||||
],
|
||||
trailing: FilterChip(
|
||||
label: Text(_useManualMileage ? 'Manual' : 'Automatic'),
|
||||
@@ -793,9 +796,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (trailing != null) trailing,
|
||||
],
|
||||
|
||||
@@ -68,17 +68,20 @@ class _TractionPageState extends State<TractionPage> {
|
||||
}
|
||||
|
||||
bool get _hasFilters {
|
||||
final dynamicFieldsUsed = _dynamicControllers.values
|
||||
.any((controller) => controller.text.trim().isNotEmpty) ||
|
||||
_enumSelections.values
|
||||
.any((value) => (value ?? '').toString().trim().isNotEmpty);
|
||||
final dynamicFieldsUsed =
|
||||
_dynamicControllers.values.any(
|
||||
(controller) => controller.text.trim().isNotEmpty,
|
||||
) ||
|
||||
_enumSelections.values.any(
|
||||
(value) => (value ?? '').toString().trim().isNotEmpty,
|
||||
);
|
||||
|
||||
return [
|
||||
_selectedClass,
|
||||
_classController.text,
|
||||
_numberController.text,
|
||||
_nameController.text,
|
||||
].any((value) => (value ?? '').toString().trim().isNotEmpty) ||
|
||||
_selectedClass,
|
||||
_classController.text,
|
||||
_numberController.text,
|
||||
_nameController.text,
|
||||
].any((value) => (value ?? '').toString().trim().isNotEmpty) ||
|
||||
dynamicFieldsUsed;
|
||||
}
|
||||
|
||||
@@ -109,7 +112,11 @@ class _TractionPageState extends State<TractionPage> {
|
||||
}
|
||||
|
||||
void _clearFilters() {
|
||||
for (final controller in [_classController, _numberController, _nameController]) {
|
||||
for (final controller in [
|
||||
_classController,
|
||||
_numberController,
|
||||
_nameController,
|
||||
]) {
|
||||
controller.clear();
|
||||
}
|
||||
for (final controller in _dynamicControllers.values) {
|
||||
@@ -135,9 +142,13 @@ class _TractionPageState extends State<TractionPage> {
|
||||
List<EventField> _activeEventFields(List<EventField> fields) {
|
||||
return fields
|
||||
.where(
|
||||
(field) =>
|
||||
!['class', 'number', 'name', 'build date', 'build_date']
|
||||
.contains(field.name.toLowerCase()),
|
||||
(field) => ![
|
||||
'class',
|
||||
'number',
|
||||
'name',
|
||||
'build date',
|
||||
'build_date',
|
||||
].contains(field.name.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
@@ -147,7 +158,10 @@ class _TractionPageState extends State<TractionPage> {
|
||||
if (field.enumValues != null) {
|
||||
_enumSelections.putIfAbsent(field.name, () => null);
|
||||
} else {
|
||||
_dynamicControllers.putIfAbsent(field.name, () => TextEditingController());
|
||||
_dynamicControllers.putIfAbsent(
|
||||
field.name,
|
||||
() => TextEditingController(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,17 +242,22 @@ class _TractionPageState extends State<TractionPage> {
|
||||
);
|
||||
},
|
||||
fieldViewBuilder:
|
||||
(context, controller, focusNode, onFieldSubmitted) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Class',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
onSubmitted: (_) => _refreshTraction(),
|
||||
);
|
||||
},
|
||||
(
|
||||
context,
|
||||
controller,
|
||||
focusNode,
|
||||
onFieldSubmitted,
|
||||
) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Class',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
onSubmitted: (_) => _refreshTraction(),
|
||||
);
|
||||
},
|
||||
optionsViewBuilder: (context, onSelected, options) {
|
||||
final optionList = options.toList();
|
||||
if (optionList.isEmpty) {
|
||||
@@ -323,7 +342,9 @@ class _TractionPageState extends State<TractionPage> {
|
||||
: Icons.expand_more,
|
||||
),
|
||||
label: Text(
|
||||
_showAdvancedFilters ? 'Hide filters' : 'More filters',
|
||||
_showAdvancedFilters
|
||||
? 'Hide filters'
|
||||
: 'More filters',
|
||||
),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
@@ -344,24 +365,26 @@ class _TractionPageState extends State<TractionPage> {
|
||||
? const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
)
|
||||
: extraFields.isEmpty
|
||||
? const Text('No extra filters available right now.')
|
||||
: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: extraFields
|
||||
.map(
|
||||
(field) => _buildFilterInput(
|
||||
context,
|
||||
field,
|
||||
isMobile,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
? const Text('No extra filters available right now.')
|
||||
: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: extraFields
|
||||
.map(
|
||||
(field) => _buildFilterInput(
|
||||
context,
|
||||
field,
|
||||
isMobile,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
secondChild: const SizedBox.shrink(),
|
||||
),
|
||||
@@ -404,8 +427,9 @@ class _TractionPageState extends State<TractionPage> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed:
|
||||
data.isTractionLoading ? null : () => _refreshTraction(append: true),
|
||||
onPressed: data.isTractionLoading
|
||||
? null
|
||||
: () => _refreshTraction(append: true),
|
||||
icon: data.isTractionLoading
|
||||
? const SizedBox(
|
||||
height: 14,
|
||||
@@ -580,7 +604,11 @@ class _TractionPageState extends State<TractionPage> {
|
||||
(Color, Color) _statusChipColors(BuildContext context, String status) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final isDark = scheme.brightness == Brightness.dark;
|
||||
Color blend(Color base, {double bgOpacity = 0.18, double fgOpacity = 0.82}) {
|
||||
Color blend(
|
||||
Color base, {
|
||||
double bgOpacity = 0.18,
|
||||
double fgOpacity = 0.82,
|
||||
}) {
|
||||
final bg = Color.alphaBlend(
|
||||
base.withOpacity(isDark ? bgOpacity + 0.07 : bgOpacity),
|
||||
scheme.surface,
|
||||
@@ -719,7 +747,10 @@ class _TractionPageState extends State<TractionPage> {
|
||||
) {
|
||||
final width = isMobile ? double.infinity : 220.0;
|
||||
if (field.enumValues != null && field.enumValues!.isNotEmpty) {
|
||||
final options = field.enumValues!.map((e) => e.toString()).toSet().toList();
|
||||
final options = field.enumValues!
|
||||
.map((e) => e.toString())
|
||||
.toSet()
|
||||
.toList();
|
||||
final currentValue = _enumSelections[field.name];
|
||||
final safeValue = options.contains(currentValue) ? currentValue : null;
|
||||
return SizedBox(
|
||||
@@ -732,14 +763,9 @@ class _TractionPageState extends State<TractionPage> {
|
||||
),
|
||||
items: [
|
||||
const DropdownMenuItem(value: null, child: Text('Any')),
|
||||
...options
|
||||
.map(
|
||||
(value) => DropdownMenuItem(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
...options.map(
|
||||
(value) => DropdownMenuItem(value: value, child: Text(value)),
|
||||
),
|
||||
],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
@@ -757,7 +783,9 @@ class _TractionPageState extends State<TractionPage> {
|
||||
TextInputType? inputType;
|
||||
if (field.type != null) {
|
||||
final type = field.type!.toLowerCase();
|
||||
if (type.contains('int') || type.contains('num') || type.contains('double')) {
|
||||
if (type.contains('int') ||
|
||||
type.contains('num') ||
|
||||
type.contains('double')) {
|
||||
inputType = const TextInputType.numberWithOptions(decimal: true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user