From cc0fd1cd84d0c933ee879bebde79b2fb61bf09e7 Mon Sep 17 00:00:00 2001 From: Supan Adit Pratama Date: Wed, 29 Jul 2020 01:11:47 +0700 Subject: [PATCH] Migrate to flutter_bloc --- android/.gitignore | 7 + .../com/example/geosmart/MainActivity.kt | 6 + ios/.gitignore | 32 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + lib/bloc/authentication/authentication.dart | 3 + .../authentication/authentication_bloc.dart | 38 +++ .../authentication/authentication_event.dart | 12 + .../authentication/authentication_state.dart | 16 ++ lib/bloc/bloc.dart | 3 + lib/bloc/position/position.dart | 3 + lib/bloc/position/position_bloc.dart | 45 ++++ lib/bloc/position/position_event.dart | 25 ++ lib/bloc/position/position_state.dart | 14 ++ lib/bloc/setting/setting.dart | 3 + lib/bloc/setting/setting_bloc.dart | 40 +++ lib/bloc/setting/setting_event.dart | 23 ++ lib/bloc/setting/setting_state.dart | 26 ++ lib/config.dart | 1 + lib/main.dart | 62 ++++- lib/model/setting.dart | 6 +- lib/page/map.dart | 208 ---------------- lib/page/map_page.dart | 139 +++++++++++ lib/page/setting.dart | 165 ------------ lib/page/setting_page.dart | 85 +++++++ lib/page/startup.dart | 60 ----- lib/page/startup_page.dart | 24 ++ lib/service/position_service.dart | 20 +- lib/service/setting_service.dart | 9 +- lib/service/unique_id_service.dart | 13 +- pubspec.lock | 235 +++++++++++++++++- pubspec.yaml | 4 +- 34 files changed, 886 insertions(+), 473 deletions(-) create mode 100644 android/.gitignore create mode 100644 android/app/src/main/kotlin/com/example/geosmart/MainActivity.kt create mode 100644 ios/.gitignore create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 lib/bloc/authentication/authentication.dart create mode 100644 lib/bloc/authentication/authentication_bloc.dart create mode 100644 lib/bloc/authentication/authentication_event.dart create mode 100644 lib/bloc/authentication/authentication_state.dart create mode 100644 lib/bloc/bloc.dart create mode 100644 lib/bloc/position/position.dart create mode 100644 lib/bloc/position/position_bloc.dart create mode 100644 lib/bloc/position/position_event.dart create mode 100644 lib/bloc/position/position_state.dart create mode 100644 lib/bloc/setting/setting.dart create mode 100644 lib/bloc/setting/setting_bloc.dart create mode 100644 lib/bloc/setting/setting_event.dart create mode 100644 lib/bloc/setting/setting_state.dart delete mode 100644 lib/page/map.dart create mode 100644 lib/page/map_page.dart delete mode 100644 lib/page/setting.dart create mode 100644 lib/page/setting_page.dart delete mode 100644 lib/page/startup.dart create mode 100644 lib/page/startup_page.dart diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..bc2100d --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,7 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java diff --git a/android/app/src/main/kotlin/com/example/geosmart/MainActivity.kt b/android/app/src/main/kotlin/com/example/geosmart/MainActivity.kt new file mode 100644 index 0000000..e9664ed --- /dev/null +++ b/android/app/src/main/kotlin/com/example/geosmart/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.geosmart + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..e96ef60 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/lib/bloc/authentication/authentication.dart b/lib/bloc/authentication/authentication.dart new file mode 100644 index 0000000..69b9858 --- /dev/null +++ b/lib/bloc/authentication/authentication.dart @@ -0,0 +1,3 @@ +export 'authentication_bloc.dart'; +export 'authentication_event.dart'; +export 'authentication_state.dart'; diff --git a/lib/bloc/authentication/authentication_bloc.dart b/lib/bloc/authentication/authentication_bloc.dart new file mode 100644 index 0000000..ea0cc31 --- /dev/null +++ b/lib/bloc/authentication/authentication_bloc.dart @@ -0,0 +1,38 @@ +import 'package:alice/alice.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geosmart/bloc/authentication/authentication_event.dart'; +import 'package:geosmart/bloc/authentication/authentication_state.dart'; +import 'package:geosmart/service/setting_service.dart'; +import 'package:meta/meta.dart'; + +class AuthenticationBloc + extends Bloc { + final Alice alice; + final Dio dio; + + AuthenticationBloc({ + @required this.alice, + @required this.dio, + }) : super(AuthenticationInitial()); + + @override + Stream mapEventToState( + AuthenticationEvent event, + ) async* { + if (event is AuthenticationStarted) { + yield AuthenticationProgress(); + final s = await SettingService().getSetting(); + if (s.isValid()) { + yield AuthenticationSuccess(); + } else { + yield AuthenticationFailed(); + } + } + + if (event is AuthenticationClear) { + await SettingService().clearSetting(); + yield AuthenticationFailed(); + } + } +} diff --git a/lib/bloc/authentication/authentication_event.dart b/lib/bloc/authentication/authentication_event.dart new file mode 100644 index 0000000..bef0b4f --- /dev/null +++ b/lib/bloc/authentication/authentication_event.dart @@ -0,0 +1,12 @@ +import 'package:equatable/equatable.dart'; + +class AuthenticationEvent extends Equatable { + const AuthenticationEvent(); + + @override + List get props => []; +} + +class AuthenticationStarted extends AuthenticationEvent {} + +class AuthenticationClear extends AuthenticationEvent {} diff --git a/lib/bloc/authentication/authentication_state.dart b/lib/bloc/authentication/authentication_state.dart new file mode 100644 index 0000000..1c5fce6 --- /dev/null +++ b/lib/bloc/authentication/authentication_state.dart @@ -0,0 +1,16 @@ +import 'package:equatable/equatable.dart'; + +class AuthenticationState extends Equatable { + const AuthenticationState(); + + @override + List get props => []; +} + +class AuthenticationInitial extends AuthenticationState {} + +class AuthenticationProgress extends AuthenticationState {} + +class AuthenticationSuccess extends AuthenticationState {} + +class AuthenticationFailed extends AuthenticationState {} diff --git a/lib/bloc/bloc.dart b/lib/bloc/bloc.dart new file mode 100644 index 0000000..ec872be --- /dev/null +++ b/lib/bloc/bloc.dart @@ -0,0 +1,3 @@ +export 'authentication/authentication.dart'; +export 'position/position.dart'; +export 'setting/setting.dart'; diff --git a/lib/bloc/position/position.dart b/lib/bloc/position/position.dart new file mode 100644 index 0000000..e6db2bb --- /dev/null +++ b/lib/bloc/position/position.dart @@ -0,0 +1,3 @@ +export 'position_bloc.dart'; +export 'position_event.dart'; +export 'position_state.dart'; diff --git a/lib/bloc/position/position_bloc.dart b/lib/bloc/position/position_bloc.dart new file mode 100644 index 0000000..43af665 --- /dev/null +++ b/lib/bloc/position/position_bloc.dart @@ -0,0 +1,45 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geosmart/bloc/authentication/authentication.dart'; +import 'package:geosmart/bloc/position/position_event.dart'; +import 'package:geosmart/bloc/position/position_state.dart'; +import 'package:geosmart/service/position_service.dart'; +import 'package:meta/meta.dart'; + +class PositionBloc extends Bloc { + final AuthenticationBloc authenticationBloc; + + PositionBloc({@required this.authenticationBloc}) + : super(PositionTrackingIdle()); + + @override + Stream mapEventToState(PositionEvent event) async* { + if (event is PositionStartTracking) { + yield PositionTrackingStarted(); + } + if (event is PositionStopTracking) { + try { + await PositionService( + dio: authenticationBloc.dio, + ).stopTracking(); + yield PositionTrackingIdle(); + } catch (e) { + yield PositionTrackingFailed(); + } + } + if (event is PositionSend) { + try { + PositionService( + dio: authenticationBloc.dio, + ).sendPosition( + event.lat, + event.lng, + ); + } catch (_) { + await PositionService( + dio: authenticationBloc.dio, + ).stopTracking(); + yield PositionTrackingFailed(); + } + } + } +} diff --git a/lib/bloc/position/position_event.dart b/lib/bloc/position/position_event.dart new file mode 100644 index 0000000..7cb98f1 --- /dev/null +++ b/lib/bloc/position/position_event.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; + +class PositionEvent extends Equatable { + const PositionEvent(); + + @override + List get props => []; +} + +class PositionStartTracking extends PositionEvent {} + +class PositionSend extends PositionEvent { + final String lat; + final String lng; + + const PositionSend({this.lat, this.lng}); + + @override + List get props => [this.lat, this.lng]; + + @override + String toString() => "PositionSend { lat: $lat, lng: $lng }"; +} + +class PositionStopTracking extends PositionEvent {} diff --git a/lib/bloc/position/position_state.dart b/lib/bloc/position/position_state.dart new file mode 100644 index 0000000..9305bf6 --- /dev/null +++ b/lib/bloc/position/position_state.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; + +class PositionState extends Equatable { + const PositionState(); + + @override + List get props => []; +} + +class PositionTrackingIdle extends PositionState {} + +class PositionTrackingFailed extends PositionState {} + +class PositionTrackingStarted extends PositionState {} diff --git a/lib/bloc/setting/setting.dart b/lib/bloc/setting/setting.dart new file mode 100644 index 0000000..36a70e7 --- /dev/null +++ b/lib/bloc/setting/setting.dart @@ -0,0 +1,3 @@ +export 'setting_bloc.dart'; +export 'setting_event.dart'; +export 'setting_state.dart'; diff --git a/lib/bloc/setting/setting_bloc.dart b/lib/bloc/setting/setting_bloc.dart new file mode 100644 index 0000000..7c7cac3 --- /dev/null +++ b/lib/bloc/setting/setting_bloc.dart @@ -0,0 +1,40 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geosmart/bloc/authentication/authentication.dart'; +import 'package:geosmart/bloc/setting/setting_event.dart'; +import 'package:geosmart/bloc/setting/setting_state.dart'; +import 'package:geosmart/model/setting.dart'; +import 'package:geosmart/service/setting_service.dart'; +import 'package:geosmart/service/unique_id_service.dart'; +import 'package:meta/meta.dart'; + +class SettingBloc extends Bloc { + final AuthenticationBloc authenticationBloc; + + SettingBloc({@required this.authenticationBloc}) : super(SettingInitial()); + + @override + Stream mapEventToState(SettingEvent event) async* { + if (event is SettingSet) { + yield SettingProgress(); + if (event.host != null && event.host != "") { + try { + final s = await UniqueIDService().getUniqueID( + event.host, + authenticationBloc.dio, + ); + SettingService().setSetting(SettingModel( + event.host, + s.id, + )); + yield SettingSuccess(); + } catch (_) { + yield SettingFailed( + message: "Make sure your host is correct and alive.", + ); + } + } else { + yield SettingFailed(message: "Host cannot null or empty."); + } + } + } +} diff --git a/lib/bloc/setting/setting_event.dart b/lib/bloc/setting/setting_event.dart new file mode 100644 index 0000000..47966bd --- /dev/null +++ b/lib/bloc/setting/setting_event.dart @@ -0,0 +1,23 @@ +import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; + +class SettingEvent extends Equatable { + const SettingEvent(); + + @override + List get props => []; +} + +class SettingSet extends SettingEvent { + final String host; + + const SettingSet({@required this.host}) : assert(host != null && host != ""); + + @override + List get props => []; + + @override + String toString() => "SettingSet { host: $host }"; +} + +class SettingClear extends SettingEvent {} diff --git a/lib/bloc/setting/setting_state.dart b/lib/bloc/setting/setting_state.dart new file mode 100644 index 0000000..03decc8 --- /dev/null +++ b/lib/bloc/setting/setting_state.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; + +class SettingState extends Equatable { + const SettingState(); + + @override + List get props => []; +} + +class SettingInitial extends SettingState {} + +class SettingProgress extends SettingState {} + +class SettingSuccess extends SettingState {} + +class SettingFailed extends SettingState { + final String message; + + const SettingFailed({this.message}); + + @override + List get props => [this.message]; + + @override + String toString() => "SettingFailed { message: $message }"; +} diff --git a/lib/config.dart b/lib/config.dart index 333f39a..e281b25 100644 --- a/lib/config.dart +++ b/lib/config.dart @@ -1,4 +1,5 @@ class Config { static const String api = "http://192.168.1.7:8080"; static const bool dynamicHostSetting = true; + static const bool showInterceptor = true; } diff --git a/lib/main.dart b/lib/main.dart index 8872fda..4ebb7df 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,19 +1,71 @@ +import 'package:alice/alice.dart'; +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:geosmart/page/startup.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geosmart/config.dart'; +import 'package:geosmart/page/map_page.dart'; +import 'package:geosmart/page/setting_page.dart'; +import 'package:geosmart/page/startup_page.dart'; -void main() => runApp(MyApp()); +import 'bloc/bloc.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + final Alice alice = Alice(showNotification: Config.showInterceptor); + final Dio dio = Dio(); + dio.interceptors.add(alice.getDioInterceptor()); + + runApp( + MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => AuthenticationBloc( + alice: alice, + dio: dio, + )..add(AuthenticationStarted()), + ), + BlocProvider( + create: (context) => SettingBloc( + authenticationBloc: BlocProvider.of( + context, + ), + ), + ), + BlocProvider( + create: (context) => PositionBloc( + authenticationBloc: BlocProvider.of( + context, + ), + ), + ), + ], + child: MyApp(), + ), + ); +} class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - SystemChrome.setEnabledSystemUIOverlays([]); return MaterialApp( title: 'Geo Smart App', + navigatorKey: BlocProvider.of( + context, + ).alice.getNavigatorKey(), theme: ThemeData( primarySwatch: Colors.blue, ), - home: Startup(), + home: BlocBuilder( + builder: (ctx, state) { + if (state is AuthenticationSuccess) { + return MapPage(); + } + if (state is AuthenticationFailed) { + return SettingPage(); + } + return StartupPage(); + }, + ), ); } } diff --git a/lib/model/setting.dart b/lib/model/setting.dart index 1c4a1a7..2a4840d 100644 --- a/lib/model/setting.dart +++ b/lib/model/setting.dart @@ -12,9 +12,7 @@ class SettingModel { return (this.id == "" || this.id == null); } - bool isNullHostId(String operator) { - return (operator == "and") - ? isNullHost() && isNullId() - : isNullHost() || isNullId(); + bool isValid() { + return (!isNullHost() && !isNullId()); } } diff --git a/lib/page/map.dart b/lib/page/map.dart deleted file mode 100644 index beddc79..0000000 --- a/lib/page/map.dart +++ /dev/null @@ -1,208 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:geolocator/geolocator.dart' as geo; -import 'package:geosmart/model/position.dart'; -import 'package:geosmart/model/setting.dart'; -import 'package:geosmart/page/setting.dart'; -import 'package:geosmart/service/position_service.dart'; -import 'package:geosmart/service/setting_service.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; - -class Map extends StatefulWidget { - Map({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MapState createState() => _MapState(); -} - -class _MapState extends State { - Completer _controller = Completer(); - double latitude = 0.0; - double longitude = 0.0; - double altitude = 0.0; - double accuracy = 0.0; - double bearing = 0.0; - double speed = 0.0; - PositionService _positionService; - SettingService _settingService; - bool isChecking = true; - Position _position; - - String id; - String host; - - bool isGranted = false; - bool isTracking = false; - - @override - void initState() { - super.initState(); - setState(() { - isChecking = true; - }); - _settingService = new SettingService(); - _positionService = new PositionService(); - // geo.Geolocator()..forceAndroidLocationManager = true; - geo.Geolocator().checkGeolocationPermissionStatus().then( - (v) { - setState(() { - isGranted = true; - }); - var geolocator = geo.Geolocator(); - var locationOptions = geo.LocationOptions( - accuracy: geo.LocationAccuracy.high, - ); - - geolocator - .getPositionStream(locationOptions) - .listen((geo.Position position) { - if (this.id != null) { - setState(() { - this._position = Position( - id: this.id, - lat: position.latitude.toString(), - lng: position.longitude.toString(), - type: "user", - ); - }); - } else { - stopLocationService(); - settingPage(); - } - print( - "New position detected with lat ${position.latitude} and lng ${position.longitude}"); - sendLastPosition(); - }); - - _settingService.getSetting().then((v) { - this.id = v.id; - this.host = v.host; - - if (v.isNullId()) { - settingPage(); - } - }); - }, - ).catchError((e) { - setState(() { - isGranted = false; - }); - }).whenComplete(() { - setState(() { - isChecking = false; - }); - }); - } - - static final CameraPosition _kGooglePlex = CameraPosition( - target: LatLng(-6.914744, 107.609810), - zoom: 14.4746, - ); - - @override - Widget build(BuildContext context) { - return new Scaffold( - body: Stack( - children: [ - GoogleMap( - mapType: MapType.normal, - initialCameraPosition: _kGooglePlex, - myLocationEnabled: true, - myLocationButtonEnabled: true, - onMapCreated: (GoogleMapController controller) { - _controller.complete(controller); - }, - ), - Container( - child: Image.asset("assets/images/logo.png"), - width: 70, - height: 70, - alignment: Alignment.topLeft, - margin: EdgeInsets.only(left: 10.0), - ), - Container( - child: FlatButton( - color: (isTracking) ? Colors.redAccent : Colors.green, - onPressed: toggleTracking, - child: Text( - (isChecking) - ? "Please wait" - : ((isTracking) ? "Stop Tracking" : "Start Tracking"), - style: TextStyle(color: Colors.white), - ), - ), - margin: EdgeInsets.only(bottom: 50.0), - alignment: Alignment.bottomCenter, - ) - ], - ), - ); - } - - toggleTracking() { - if (!isChecking) { - if (isGranted) { - if (isTracking) { - stopLocationService(); - } else { - sendLastPosition(); - } - setState(() { - isTracking = !isTracking; - }); - } else { - FlutterToast.showToast( - msg: "Make sure you have turn on location services", - ); - } - } - } - - stopLocationService() { - if (_positionService != null) { - _positionService.stopTracking(); - } - } - - settingPage() { - Navigator.of(context).pushReplacement(new MaterialPageRoute( - builder: (BuildContext context) => Setting(), - )); - } - - sendLastPosition() async { - SettingModel m = await _settingService.getSetting(); - print("Prepare to Send last location with ID : ${m.id} to ${m.host}"); - if (this._position != null) { - if (this._position.isValid()) { - if (_positionService != null) { - print("Lat ${_position.lat}, Lng ${_position.lng}"); - if (isTracking) { - print("Send tracking location"); - _positionService.sendPosition( - this._position.lat.toString(), - this._position.lng.toString(), - ); - } - } else { - FlutterToast.showToast( - msg: "Failed to start position service", - ); - settingPage(); - } - } else { - print("Invalid Position"); - } - } - } - - @override - void dispose() { - super.dispose(); - stopLocationService(); - } -} diff --git a/lib/page/map_page.dart b/lib/page/map_page.dart new file mode 100644 index 0000000..d3bcfd6 --- /dev/null +++ b/lib/page/map_page.dart @@ -0,0 +1,139 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geolocator/geolocator.dart' as geo; +import 'package:geosmart/bloc/bloc.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +class MapPage extends StatefulWidget { + MapPage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _MapPageState createState() => _MapPageState(); +} + +class _MapPageState extends State { + Completer _controller = Completer(); + bool isChecking = true; + bool isGranted = false; + bool isTracking = false; + + @override + void initState() { + super.initState(); + setState(() { + isChecking = true; + }); + // geo.Geolocator()..forceAndroidLocationManager = true; + geo.Geolocator().checkGeolocationPermissionStatus().then( + (v) { + isGranted = true; + var geolocator = geo.Geolocator(); + var locationOptions = geo.LocationOptions( + accuracy: geo.LocationAccuracy.high, + ); + + geolocator.getPositionStream(locationOptions).listen( + (geo.Position position) { + if (isTracking && isGranted) { + BlocProvider.of(context).add( + PositionSend( + lat: position.latitude.toString(), + lng: position.longitude.toString(), + ), + ); + } + }, + ); + }, + ).catchError((e) { + isGranted = false; + }).whenComplete(() { + setState(() { + isChecking = false; + }); + }); + } + + static final CameraPosition _kGooglePlex = CameraPosition( + target: LatLng(-6.914744, 107.609810), + zoom: 14.4746, + ); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: BlocListener( + listener: (ctx, state) { + if (state is PositionTrackingFailed) { + BlocProvider.of(ctx).add( + AuthenticationClear(), + ); + } + if (state is PositionTrackingStarted) { + isTracking = true; + } + if (state is PositionTrackingIdle) { + isTracking = false; + } + }, + child: BlocBuilder( + builder: (ctx, state) { + return Stack( + children: [ + GoogleMap( + mapType: MapType.normal, + initialCameraPosition: _kGooglePlex, + myLocationEnabled: true, + myLocationButtonEnabled: true, + onMapCreated: (GoogleMapController controller) { + _controller.complete(controller); + }, + ), + Container( + child: Image.asset("assets/images/logo.png"), + width: 70, + height: 70, + alignment: Alignment.topLeft, + margin: EdgeInsets.only(left: 10.0), + ), + Container( + child: FlatButton( + color: (state is PositionTrackingStarted) + ? Colors.redAccent + : Colors.green, + onPressed: () async { + if (state is PositionTrackingStarted) { + BlocProvider.of(ctx).add( + PositionStopTracking(), + ); + } + if (state is PositionTrackingIdle) { + BlocProvider.of(ctx).add( + PositionStartTracking(), + ); + } + }, + child: Text( + (isChecking) + ? "Please wait" + : (state is PositionTrackingStarted + ? "Stop Tracking" + : "Start Tracking"), + style: TextStyle(color: Colors.white), + ), + ), + margin: EdgeInsets.only(bottom: 50.0), + alignment: Alignment.bottomCenter, + ) + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/page/setting.dart b/lib/page/setting.dart deleted file mode 100644 index 7c530c6..0000000 --- a/lib/page/setting.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:geosmart/config.dart'; -import 'package:geosmart/model/setting.dart'; -import 'package:geosmart/page/map.dart'; -import 'package:geosmart/service/setting_service.dart'; -import 'package:geosmart/service/unique_id_service.dart'; - -class Setting extends StatefulWidget { - @override - State createState() { - return new _SettingState(); - } -} - -class _SettingState extends State { - final _hostController = TextEditingController(); - SettingService _settingBloc; - UniqueIDService _uniqueIDBloc; - String id; - String host; - - @override - void initState() { - _settingBloc = new SettingService(); - - if (!Config.dynamicHostSetting) { - _settingBloc.setSetting(new SettingModel(Config.api, null)); - } - - _settingBloc.getSetting().then((settingModel) { - if (!settingModel.isNullId()) { - this.id = settingModel.id; - } - if (!settingModel.isNullHost()) { - this.host = settingModel.host; - } - - _hostController.text = this.host; - - if (!settingModel.isNullHost()) { - _uniqueIDBloc = new UniqueIDService(settingModel); - - if (_uniqueIDBloc != null) { - this._uniqueIDBloc.getUniqueID().then((uniqueId) { - print("Your Unique ID " + uniqueId.id.toString()); - if (uniqueId.id != null && uniqueId.id != "") { - if (!settingModel.isNullId()) { - mapPage(); - } else { - this._settingBloc.setSetting( - new SettingModel(this._hostController.text, uniqueId.id), - ); - mapPage(); - } - } else { - FlutterToast.showToast(msg: "Invalid host address"); - } - }); - } - } - }); - super.initState(); - } - - mapPage() { - Navigator.of(context).pushReplacement(new MaterialPageRoute( - builder: (BuildContext context) => Map(), - )); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - child: Center( - child: Container( - child: Column( - children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/images/server.png"), - ), - ), - height: 200, - ), - SizedBox( - height: 30, - ), - TextField( - controller: this._hostController, - decoration: InputDecoration( - labelText: "Host", - border: OutlineInputBorder(), - ), - ), - SizedBox( - height: 10.0, - ), - Container( - width: double.infinity, - child: FlatButton( - color: Colors.blueAccent, - onPressed: () { - this - ._settingBloc - .setSetting( - new SettingModel(this._hostController.text, null), - ) - .then((settingModel) { - if (!settingModel.isNullId()) { - this.id = settingModel.id; - } - if (!settingModel.isNullHost()) { - this.host = settingModel.host; - } - - _hostController.text = this.host; - - if (!settingModel.isNullHost()) { - _uniqueIDBloc = new UniqueIDService(settingModel); - - if (_uniqueIDBloc != null) { - this._uniqueIDBloc.getUniqueID().then((uniqueId) { - print("Your Unique ID " + uniqueId.id.toString()); - if (uniqueId.id != null && uniqueId.id != "") { - if (!settingModel.isNullId()) { - mapPage(); - } else { - this._settingBloc.setSetting( - new SettingModel( - this._hostController.text, - uniqueId.id, - ), - ); - mapPage(); - } - } else { - FlutterToast.showToast( - msg: "Invalid host address", - ); - } - }); - } - } - }); - }, - child: Text( - "Save", - style: TextStyle(color: Colors.white), - ), - ), - ) - ], - ), - width: 200, - height: 400, - ), - ), - ), - ); - } -} diff --git a/lib/page/setting_page.dart b/lib/page/setting_page.dart new file mode 100644 index 0000000..d6f7487 --- /dev/null +++ b/lib/page/setting_page.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geosmart/bloc/bloc.dart'; + +class SettingPage extends StatefulWidget { + @override + State createState() { + return new _SettingPageState(); + } +} + +class _SettingPageState extends State { + final _hostController = TextEditingController(); + final GlobalKey _scaffoldKey = new GlobalKey(); + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + body: BlocListener( + listener: (ctx, state) { + if (state is SettingFailed) { + _scaffoldKey.currentState.showSnackBar(SnackBar( + content: Text(state.message), + duration: Duration(seconds: 3), + )); + } + if (state is SettingSuccess) { + BlocProvider.of(ctx).add( + AuthenticationStarted(), + ); + } + }, + child: Container( + child: Center( + child: Container( + child: Column( + children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("assets/images/server.png"), + ), + ), + height: 200, + ), + SizedBox( + height: 30, + ), + TextField( + controller: this._hostController, + decoration: InputDecoration( + labelText: "Host", + border: OutlineInputBorder(), + ), + ), + SizedBox( + height: 10.0, + ), + Container( + width: double.infinity, + child: FlatButton( + color: Colors.blueAccent, + onPressed: () { + BlocProvider.of(context).add( + SettingSet(host: _hostController.text), + ); + }, + child: Text( + "Save", + style: TextStyle(color: Colors.white), + ), + ), + ) + ], + ), + width: 200, + height: 400, + ), + ), + ), + ), + ); + } +} diff --git a/lib/page/startup.dart b/lib/page/startup.dart deleted file mode 100644 index 1fdd7c0..0000000 --- a/lib/page/startup.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:geosmart/component/loader.dart'; -import 'package:geosmart/page/map.dart'; -import 'package:geosmart/page/setting.dart'; -import 'package:geosmart/service/setting_service.dart'; - -class Startup extends StatefulWidget { - @override - State createState() { - return new _StartupState(); - } -} - -class _StartupState extends State { - SettingService _settingService; - - @override - void initState() { - _settingService = new SettingService(); - - _settingService.getSetting().then((settingModel) { - if (settingModel.isNullHost() && settingModel.isNullId()) { - Navigator.of(context).pushReplacement(new MaterialPageRoute( - builder: (BuildContext context) => Setting(), - )); - } else { - Navigator.of(context).pushReplacement(new MaterialPageRoute( - builder: (BuildContext context) => Map(), - )); - } - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Container( - child: Column( - children: [ - Loader(), - SizedBox( - height: 20.0, - ), - Text("Preparing System") - ], - ), - height: 50, - ), - ), - ); - } - - @override - void dispose() { - super.dispose(); - } -} diff --git a/lib/page/startup_page.dart b/lib/page/startup_page.dart new file mode 100644 index 0000000..cb3dbee --- /dev/null +++ b/lib/page/startup_page.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:geosmart/component/loader.dart'; + +class StartupPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + child: Column( + children: [ + Loader(), + SizedBox( + height: 20.0, + ), + Text("Preparing System") + ], + ), + height: 50, + ), + ), + ); + } +} diff --git a/lib/service/position_service.dart b/lib/service/position_service.dart index 90e7e5c..a78d2ce 100644 --- a/lib/service/position_service.dart +++ b/lib/service/position_service.dart @@ -2,12 +2,15 @@ import 'package:dio/dio.dart'; import 'package:geosmart/model/position.dart'; import 'package:geosmart/model/response.dart'; import 'package:geosmart/service/setting_service.dart'; +import 'package:meta/meta.dart'; class PositionService { - final Dio _dio = Dio(); + final Dio dio; final SettingService _settingBloc = SettingService(); - PositionService(); + PositionService({ + @required this.dio, + }); Future sendPosition(String lat, String lng) async { var m = await _settingBloc.getSetting(); @@ -18,25 +21,20 @@ class PositionService { lng: lng, ); try { - Response response = await _dio.post( + Response response = await dio.post( m.host + "/point/set", data: position.toJson(), ); return ResponseModel.fromJson(response.data); } on DioError catch (e) { - print(e.response); - if (e.response != null) { - return ResponseModel.fromJson(e.response.data); - } else { - return ResponseModel.fromNull(); - } + throw (e); } } Future stopTracking() async { var m = await _settingBloc.getSetting(); try { - Response response = await _dio.post( + Response response = await dio.post( m.host + "/point/unset", data: { "id": m.id, @@ -45,7 +43,7 @@ class PositionService { ); return ResponseModel.fromJson(response.data); } on DioError catch (e) { - return ResponseModel.fromJson(e.response.data); + throw (e); } } } diff --git a/lib/service/setting_service.dart b/lib/service/setting_service.dart index fcf0b7c..2d06169 100644 --- a/lib/service/setting_service.dart +++ b/lib/service/setting_service.dart @@ -16,9 +16,16 @@ class SettingService { return await getSetting(); } + Future clearSetting() async { + SharedPreferences sharedPreferences = await this.getSharedPreferences(); + sharedPreferences.remove(_host); + sharedPreferences.remove(_id); + return await getSetting(); + } + Future getSetting() async { SharedPreferences sharedPreferences = await this.getSharedPreferences(); - return new SettingModel( + return SettingModel( sharedPreferences.getString(_host), sharedPreferences.getString(_id), ); diff --git a/lib/service/unique_id_service.dart b/lib/service/unique_id_service.dart index 09ac0ff..567ecaf 100644 --- a/lib/service/unique_id_service.dart +++ b/lib/service/unique_id_service.dart @@ -1,22 +1,17 @@ import 'package:dio/dio.dart'; -import 'package:geosmart/model/setting.dart'; import 'package:geosmart/model/unique_id_model.dart'; class UniqueIDService { final String _endpoint = "/id/get/unique"; - final Dio _dio = Dio(); - SettingModel _settingModel; - UniqueIDService(this._settingModel); - - Future getUniqueID() async { + Future getUniqueID(String host, Dio dio) async { try { - Response response = await _dio.get( - this._settingModel.host + _endpoint, + Response response = await dio.get( + host + _endpoint, ); return UniqueIDModel.fromJson(response.data); } on DioError catch (e) { - return UniqueIDModel.error(); + throw (e); } } } diff --git a/pubspec.lock b/pubspec.lock index a18a715..7b96cb9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + alice: + dependency: "direct main" + description: + name: alice + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" archive: dependency: transitive description: @@ -22,6 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.1" + bloc: + dependency: transitive + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" boolean_selector: dependency: transitive description: @@ -36,6 +50,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.3" + chewie: + dependency: transitive + description: + name: chewie + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.10" + chopper: + dependency: transitive + description: + name: chopper + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.3" collection: dependency: transitive description: @@ -72,17 +100,52 @@ packages: source: hosted version: "3.0.9" equatable: - dependency: transitive + dependency: "direct main" description: name: equatable url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.3" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + flutter_local_notifications: + dependency: transitive + description: + name: flutter_local_notifications + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4+2" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -100,13 +163,6 @@ packages: description: flutter source: sdk version: "0.0.0" - fluttertoast: - dependency: "direct main" - description: - name: fluttertoast - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.1" geolocator: dependency: "direct main" description: @@ -156,6 +212,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.12" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" location_permissions: dependency: transitive description: @@ -163,6 +226,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0+1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.4" matcher: dependency: transitive description: @@ -177,6 +247,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.8" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" + open_file: + dependency: transitive + description: + name: open_file + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + open_iconic_flutter: + dependency: transitive + description: + name: open_iconic_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + package_info: + dependency: transitive + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.1" path: dependency: transitive description: @@ -184,6 +282,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.6.4" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+3" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" pedantic: dependency: transitive description: @@ -191,6 +317,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.0" + permission_handler: + dependency: transitive + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.1+1" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" petitparser: dependency: transitive description: @@ -198,6 +338,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" plugin_platform_interface: dependency: transitive description: @@ -205,6 +352,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.13" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.1" quiver: dependency: transitive description: @@ -219,6 +380,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.24.1" + sensors: + dependency: transitive + description: + name: sensors + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2+2" + shake: + dependency: transitive + description: + name: shake + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + share: + dependency: transitive + description: + name: share + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4+3" shared_preferences: dependency: "direct main" description: @@ -315,6 +497,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.8" + video_player: + dependency: transitive + description: + name: video_player + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.11+2" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + video_player_web: + dependency: transitive + description: + name: video_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3+2" + wakelock: + dependency: transitive + description: + name: wakelock + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4+2" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6846b06..20f20bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,7 +34,9 @@ dependencies: shared_preferences: ^0.5.7+3 rxdart: ^0.24.1 dio: ^3.0.9 - fluttertoast: ^5.0.1 + flutter_bloc: ^6.0.1 + equatable: ^1.2.3 + alice: ^0.1.3 dev_dependencies: flutter_test: