import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lastorado_shared/lastorado_shared.dart'; import 'package:mini_server/mini_server.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'main.g.dart'; final container = ProviderContainer(); @riverpod Future getIpAddress(final ref) async { var ipAddress = ''; await NetworkInterface.list(type: InternetAddressType.IPv4) .then((interfaces) { interfaces.forEach((interface) { interface.addresses.forEach((address) { if (address.address.isNotEmpty) { ipAddress = address.address; } }); }); }); return ipAddress; } @riverpod class Taxameter extends _$Taxameter { @override TaxameterStatus build() { return TaxameterStatus( eventList: [ TripStatus(dateTime: DateTime.now(), type: TripStatusType.driving) ], hasTrailer: false, hasBigLuggage: false, isFarAway: false, isCharity: false); } void setEventList(List eventList) { state = state.copyWith(eventList: eventList); } void setHasTrailer(bool hasTrailer) { state = state.copyWith(hasTrailer: hasTrailer); } void setHasBigLuggage(bool hasBigLuggage) { state = state.copyWith(hasBigLuggage: hasBigLuggage); } void setIsFarAway(bool isFarAway) { state = state.copyWith(isFarAway: isFarAway); } void setIsCharity(bool isCharity) { state = state.copyWith(isCharity: isCharity); } } final curTripProvider = StateProvider((ref) => TripStatus(dateTime: DateTime.now(), type: TripStatusType.driving)); class TripEventList extends Notifier> { @override List build() { return []; } void add(TripStatus tripEvent) { state = [...state, tripEvent]; } void removeItem(TripStatus event) { state.remove(event); } } final tripEventListProvider = NotifierProvider>(TripEventList.new); final allEventsProvider = Provider>((ref) { final tripEvents = ref.watch(tripEventListProvider); return tripEvents; }); final timeProvider = Provider>((ref) { final tripEvents = ref.watch(tripEventListProvider); //final times = LocalTaxameterRepository.calculateTimes(tripEvents); return {'test': 1}; }); void main() async { WidgetsFlutterBinding.ensureInitialized(); final miniServer = MiniServer( host: '0.0.0.0', port: 8080, ); miniServer.get('/taxameter', (HttpRequest request) async { request.response.headers.contentType = ContentType.json; var tax = container.read(taxameterProvider); return request.response.write(jsonEncode(tax.toJson())); }); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { @override void dispose() { super.dispose(); container.dispose(); } @override Widget build(BuildContext context) { return UncontrolledProviderScope( container: container, child: const MaterialApp( home: Scaffold( body: Center( child: ContentWidget(), ), ), ), ); } } class ContentWidget extends ConsumerWidget { const ContentWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final ipAddress = ref.watch(getIpAddressProvider); final tax = ref.watch(taxameterProvider); final curEvent = ref.watch(curTripProvider); final tripEvents = ref.watch(tripEventListProvider); final times = ref.watch(timeProvider); return ListView( children: [ switch (ipAddress) { AsyncError(:final error) => ListTile( title: Text('Error: $error'), tileColor: Colors.red[200], ), AsyncData(:final value) => ListTile( title: Text('Server is listening on $value'), tileColor: Colors.grey[200], ), _ => const CircularProgressIndicator(), }, const SizedBox(height: 40), CheckboxListTile( value: tax.hasTrailer, onChanged: (value) => ref .read(taxameterProvider.notifier) .setHasTrailer(value ?? false), title: const Text('has trailer'), ), CheckboxListTile( value: tax.hasBigLuggage, onChanged: (value) => ref .read(taxameterProvider.notifier) .setHasBigLuggage(value ?? false), title: const Text('has big luggage'), ), CheckboxListTile( value: tax.isFarAway, onChanged: (value) => ref.read(taxameterProvider.notifier).setIsFarAway(value ?? false), title: const Text('is far away'), ), CheckboxListTile( value: tax.isCharity, onChanged: (value) => ref.read(taxameterProvider.notifier).setIsCharity(value ?? false), title: const Text('is charity'), ), const SizedBox( height: 10, ), Text('Trip events', style: Theme.of(context).textTheme.displayMedium), Container( decoration: BoxDecoration(color: Colors.grey[200]), child: Column( children: [ ElevatedButton( onPressed: () async { final TimeOfDay? time = await showTimePicker( context: context, initialTime: TimeOfDay.now()); final now = DateTime.now(); if (time != null) { ref.read(curTripProvider.notifier).update((state) => state = TripStatus( dateTime: DateTime(now.year, now.month, now.day, time.hour, time.minute), type: TripStatusType.driving)); } }, child: const Text('Select time')), Text('Selected time: ${curEvent.dateTime}'), DropdownMenu( dropdownMenuEntries: const [ DropdownMenuEntry( value: TripStatusType.driving, label: 'driving', ), DropdownMenuEntry( value: TripStatusType.waiting, label: 'waiting', ), DropdownMenuEntry( value: TripStatusType.done, label: 'done', ), ], onSelected: (value) { ref.read(curTripProvider.notifier).update((state) => state = TripStatus( dateTime: state.dateTime, type: value as TripStatusType)); }, ), ElevatedButton( onPressed: () { ref .read(tripEventListProvider.notifier) .add(ref.read(curTripProvider.notifier).state.copyWith()); }, child: const Text('Add event'), ), ], ), ), for (final event in tripEvents) ListTile( title: Text(event.dateTime.toString()), subtitle: Text(event.type.toString()), trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () { ref.read(tripEventListProvider.notifier).removeItem(event); }, ), ), Container( decoration: BoxDecoration(color: Colors.grey[200]), child: Column( children: [Text('Times: $times')], ), ) ], ); } }