You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.5 KiB
209 lines
6.5 KiB
import 'dart:convert'; |
|
|
|
import 'package:flutter/widgets.dart'; |
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_cupertino.dart'; |
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_material.dart'; |
|
import 'package:info_tren/models/train_operator_lines.dart'; |
|
import 'package:info_tren/models/ui_design.dart'; |
|
import 'package:info_tren/utils/default_ui_design.dart'; |
|
import 'package:tuple/tuple.dart'; |
|
|
|
class SelectTrainSuggestions extends StatefulWidget { |
|
final UiDesign? uiDesign; |
|
final String userInput; |
|
final void Function(int trainNumber) onTrainSelected; |
|
|
|
const SelectTrainSuggestions({ Key? key, required this.uiDesign, required this.userInput, required this.onTrainSelected }) : super(key: key); |
|
|
|
@override |
|
SelectTrainSuggestionsState createState() { |
|
final uiDesign = this.uiDesign ?? defaultUiDesign; |
|
switch(uiDesign) { |
|
case UiDesign.MATERIAL: |
|
return SelectTrainSuggestionsStateMaterial(); |
|
case UiDesign.CUPERTINO: |
|
return SelectTrainSuggestionsStateCupertino(); |
|
default: |
|
throw UnmatchedUiDesignException(uiDesign); |
|
} |
|
} |
|
} |
|
|
|
abstract class SelectTrainSuggestionsState extends State<SelectTrainSuggestions> { |
|
late String userInput; |
|
|
|
List<TrainOperatorLines> operators = []; |
|
|
|
Future loadOperators(BuildContext context) async { |
|
operators = []; |
|
|
|
final operatorsString = await DefaultAssetBundle.of(context).loadString("assets/lines/files.txt"); |
|
final operatorsFilesList = operatorsString.split("\n"); |
|
|
|
final decoder = JsonDecoder(); |
|
|
|
for (final operatorFile in operatorsFilesList) { |
|
final operatorString = await DefaultAssetBundle.of(context).loadString("assets/lines/$operatorFile"); |
|
final operatorData = decoder.convert(operatorString); |
|
final _operator = TrainOperatorLines.fromJson(operatorData); |
|
operators.add(_operator); |
|
} |
|
} |
|
|
|
@override |
|
void initState() { |
|
super.initState(); |
|
userInput = widget.userInput; |
|
|
|
loadOperators(context).then((_) { |
|
setState(() {}); |
|
}); |
|
} |
|
|
|
@override |
|
void didChangeDependencies() { |
|
super.didChangeDependencies(); |
|
if (userInput != widget.userInput) { |
|
setState(() { |
|
userInput = widget.userInput; |
|
}); |
|
} |
|
} |
|
|
|
String getUseCurrentInputWidgetText(int currentInput) => 'Caută trenul cu numărul $currentInput'; |
|
Widget getUseCurrentInputWidget(int currentInput, void Function(int) onTrainSelected); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
var sliversTuple = operators.map( |
|
(op) => Tuple2( |
|
getFilteredLines(op, userInput), |
|
op.operator, |
|
) |
|
).where((tuple) => tuple.item1.isNotEmpty).toList(); |
|
if (userInput.isNotEmpty) sliversTuple.sort((a, b) { |
|
final aTrain = a.item1.first; |
|
final bTrain = b.item1.first; |
|
|
|
final inputAsRegExp = RegExp(userInput); |
|
|
|
final matchOnA = inputAsRegExp.firstMatch(aTrain.number)!; |
|
final matchOnB = inputAsRegExp.firstMatch(bTrain.number)!; |
|
|
|
if (matchOnA.start != matchOnB.start) return matchOnA.start - matchOnB.start; |
|
|
|
if (aTrain.number.length != bTrain.number.length) return aTrain.number.length - bTrain.number.length; |
|
|
|
return aTrain.number.compareTo(bTrain.number); |
|
}); |
|
var slivers = sliversTuple.map((tuple) => OperatorAutocompleteSliver( |
|
uiDesign: widget.uiDesign, |
|
operatorName: tuple.item2, |
|
trains: tuple.item1, |
|
onTrainSelected: widget.onTrainSelected, |
|
)).toList(); |
|
|
|
return CustomScrollView( |
|
slivers: <Widget>[ |
|
...slivers, |
|
SliverToBoxAdapter( |
|
child: int.tryParse(userInput) != null ? getUseCurrentInputWidget(int.parse(userInput), widget.onTrainSelected) : Container(), |
|
), |
|
SliverToBoxAdapter( |
|
child: Container( |
|
height: MediaQuery.of(context).viewPadding.bottom, |
|
), |
|
), |
|
], |
|
); |
|
} |
|
|
|
List<TrainOperatorTrainDescription> getFilteredLines(TrainOperatorLines _operator, String currentInput) { |
|
if (currentInput.isNotEmpty) { |
|
final filteredLines = _operator.trains |
|
.where((elem) => elem.number.contains(currentInput)) |
|
.toList(); |
|
|
|
filteredLines.sort((a, b) { |
|
final inputAsRegExp = RegExp(currentInput); |
|
|
|
final matchOnA = inputAsRegExp.firstMatch(a.number)!; |
|
final matchOnB = inputAsRegExp.firstMatch(b.number)!; |
|
|
|
if (matchOnA.start != matchOnB.start) return matchOnA.start - matchOnB.start; |
|
|
|
if (a.number.length != b.number.length) return a.number.length - b.number.length; |
|
|
|
return a.number.compareTo(b.number); |
|
}); |
|
|
|
return filteredLines; |
|
} |
|
else { |
|
return _operator.trains; |
|
} |
|
} |
|
} |
|
|
|
class OperatorAutocompleteSliver extends StatelessWidget { |
|
final UiDesign? uiDesign; |
|
final String operatorName; |
|
final List<TrainOperatorTrainDescription> trains; |
|
final void Function(int) onTrainSelected; |
|
|
|
const OperatorAutocompleteSliver({ Key? key, required this.uiDesign, required this.operatorName, required this.trains, required this.onTrainSelected }) : super(key: key); |
|
|
|
Widget mapTrainToItem(TrainOperatorTrainDescription train) { |
|
final uiDesign = this.uiDesign ?? defaultUiDesign; |
|
switch (uiDesign) { |
|
case UiDesign.MATERIAL: |
|
return OperatorAutocompleteTileMaterial( |
|
onTrainSelected: onTrainSelected, |
|
operatorName: operatorName, |
|
train: train, |
|
); |
|
case UiDesign.CUPERTINO: |
|
return OperatorAutocompleteTileCupertino( |
|
onTrainSelected: onTrainSelected, |
|
operatorName: operatorName, |
|
train: train, |
|
); |
|
default: |
|
throw UnmatchedUiDesignException(uiDesign); |
|
} |
|
} |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
if (trains.isEmpty) { |
|
return SliverToBoxAdapter(child: Container(),); |
|
} |
|
|
|
return SliverPrototypeExtentList( |
|
prototypeItem: Column( |
|
children: <Widget>[ |
|
mapTrainToItem(TrainOperatorTrainDescription()), |
|
], |
|
), |
|
delegate: SliverChildBuilderDelegate( |
|
(context, index) { |
|
return Column( |
|
children: <Widget>[ |
|
mapTrainToItem(trains[index]), |
|
], |
|
); |
|
}, |
|
childCount: trains.length, |
|
addSemanticIndexes: true, |
|
), |
|
); |
|
} |
|
} |
|
|
|
abstract class OperatorAutocompleteTile extends StatelessWidget { |
|
final String operatorName; |
|
final TrainOperatorTrainDescription train; |
|
final void Function(int) onTrainSelected; |
|
|
|
const OperatorAutocompleteTile({ Key? key, required this.onTrainSelected, required this.operatorName, required this.train }) : super(key: key); |
|
}
|
|
|