Allow multiple map routes to share the Map widget and transition between each other
This commit is contained in:
parent
c6090a307a
commit
10826e79b2
|
@ -1,13 +1,33 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:furman_now/src/screens/events/index.dart';
|
import 'package:furman_now/src/screens/events/index.dart';
|
||||||
import 'package:furman_now/src/screens/home/home_header.dart';
|
import 'package:furman_now/src/screens/home/home_header.dart';
|
||||||
import 'package:furman_now/src/screens/home/index.dart';
|
import 'package:furman_now/src/screens/home/index.dart';
|
||||||
import 'package:furman_now/src/screens/info/index.dart';
|
import 'package:furman_now/src/screens/info/index.dart';
|
||||||
import 'package:furman_now/src/screens/map/index.dart';
|
import 'package:furman_now/src/screens/map/index.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/map_category.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/map_home.dart';
|
||||||
import 'package:furman_now/src/screens/student_id/index.dart';
|
import 'package:furman_now/src/screens/student_id/index.dart';
|
||||||
|
import 'package:furman_now/src/utils/translucent_route.dart';
|
||||||
|
|
||||||
import '../layouts/main/index.dart';
|
import '../layouts/main/index.dart';
|
||||||
|
|
||||||
|
Route<T> mapRouteBuilder<T>(BuildContext context, Widget child, CustomPage<T> page){
|
||||||
|
return TranslucentRoute(
|
||||||
|
settings: page,
|
||||||
|
transitionDuration: const Duration(milliseconds: 200),
|
||||||
|
transitionBuilder: (context, animation, secondaryAnimation, child) =>
|
||||||
|
FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: secondaryAnimation.drive(Tween<double>(begin: 1, end: 0)),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pageBuilder: (context) => child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@MaterialAutoRouter(
|
@MaterialAutoRouter(
|
||||||
replaceInRouteName: 'Screen,Route',
|
replaceInRouteName: 'Screen,Route',
|
||||||
routes: <AutoRoute>[
|
routes: <AutoRoute>[
|
||||||
|
@ -25,7 +45,18 @@ import '../layouts/main/index.dart';
|
||||||
transitionsBuilder: TransitionsBuilders.fadeIn,
|
transitionsBuilder: TransitionsBuilders.fadeIn,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
AutoRoute(path: "map", page: MapScreen),
|
AutoRoute(path: "map", page: MapScreen, children: [
|
||||||
|
CustomRoute(
|
||||||
|
path: "",
|
||||||
|
page: MapHomeScreen,
|
||||||
|
customRouteBuilder: mapRouteBuilder,
|
||||||
|
),
|
||||||
|
CustomRoute(
|
||||||
|
path: "category/:id",
|
||||||
|
page: MapCategoryScreen,
|
||||||
|
customRouteBuilder: mapRouteBuilder,
|
||||||
|
),
|
||||||
|
]),
|
||||||
AutoRoute(path: "events", page: EventsScreen),
|
AutoRoute(path: "events", page: EventsScreen),
|
||||||
AutoRoute(path: "info", page: InfoScreen),
|
AutoRoute(path: "info", page: InfoScreen),
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
|
||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
import 'package:auto_route/auto_route.dart' as _i8;
|
import 'package:auto_route/auto_route.dart' as _i10;
|
||||||
import 'package:flutter/material.dart' as _i9;
|
import 'package:flutter/material.dart' as _i11;
|
||||||
|
|
||||||
import '../layouts/main/index.dart' as _i1;
|
import '../layouts/main/index.dart' as _i1;
|
||||||
import '../screens/events/index.dart' as _i4;
|
import '../screens/events/index.dart' as _i4;
|
||||||
|
@ -20,76 +20,106 @@ import '../screens/home/home_header.dart' as _i6;
|
||||||
import '../screens/home/index.dart' as _i2;
|
import '../screens/home/index.dart' as _i2;
|
||||||
import '../screens/info/index.dart' as _i5;
|
import '../screens/info/index.dart' as _i5;
|
||||||
import '../screens/map/index.dart' as _i3;
|
import '../screens/map/index.dart' as _i3;
|
||||||
|
import '../screens/map/map_category.dart' as _i9;
|
||||||
|
import '../screens/map/map_home.dart' as _i8;
|
||||||
|
import '../screens/map/state.dart' as _i13;
|
||||||
import '../screens/student_id/index.dart' as _i7;
|
import '../screens/student_id/index.dart' as _i7;
|
||||||
|
import 'index.dart' as _i12;
|
||||||
|
|
||||||
class AppRouter extends _i8.RootStackRouter {
|
class AppRouter extends _i10.RootStackRouter {
|
||||||
AppRouter([_i9.GlobalKey<_i9.NavigatorState>? navigatorKey])
|
AppRouter([_i11.GlobalKey<_i11.NavigatorState>? navigatorKey])
|
||||||
: super(navigatorKey);
|
: super(navigatorKey);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Map<String, _i8.PageFactory> pagesMap = {
|
final Map<String, _i10.PageFactory> pagesMap = {
|
||||||
MainLayout.name: (routeData) {
|
MainLayout.name: (routeData) {
|
||||||
return _i8.MaterialPageX<dynamic>(
|
return _i10.MaterialPageX<dynamic>(
|
||||||
routeData: routeData, child: const _i1.MainLayout());
|
routeData: routeData, child: const _i1.MainLayout());
|
||||||
},
|
},
|
||||||
HomePageRouter.name: (routeData) {
|
HomePageRouter.name: (routeData) {
|
||||||
return _i8.MaterialPageX<dynamic>(
|
return _i10.MaterialPageX<dynamic>(
|
||||||
routeData: routeData, child: const _i2.HomeScreen());
|
routeData: routeData, child: const _i2.HomeScreen());
|
||||||
},
|
},
|
||||||
MapRoute.name: (routeData) {
|
MapRoute.name: (routeData) {
|
||||||
return _i8.MaterialPageX<dynamic>(
|
return _i10.MaterialPageX<dynamic>(
|
||||||
routeData: routeData, child: const _i3.MapScreen());
|
routeData: routeData, child: const _i3.MapScreen());
|
||||||
},
|
},
|
||||||
EventsRoute.name: (routeData) {
|
EventsRoute.name: (routeData) {
|
||||||
return _i8.MaterialPageX<dynamic>(
|
return _i10.MaterialPageX<dynamic>(
|
||||||
routeData: routeData, child: const _i4.EventsScreen());
|
routeData: routeData, child: const _i4.EventsScreen());
|
||||||
},
|
},
|
||||||
InfoRoute.name: (routeData) {
|
InfoRoute.name: (routeData) {
|
||||||
return _i8.MaterialPageX<dynamic>(
|
return _i10.MaterialPageX<dynamic>(
|
||||||
routeData: routeData, child: const _i5.InfoScreen());
|
routeData: routeData, child: const _i5.InfoScreen());
|
||||||
},
|
},
|
||||||
HomeRoute.name: (routeData) {
|
HomeRoute.name: (routeData) {
|
||||||
return _i8.CustomPage<dynamic>(
|
return _i10.CustomPage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: const _i6.HomePageHeader(),
|
child: const _i6.HomePageHeader(),
|
||||||
transitionsBuilder: _i8.TransitionsBuilders.fadeIn,
|
transitionsBuilder: _i10.TransitionsBuilders.fadeIn,
|
||||||
opaque: true,
|
opaque: true,
|
||||||
barrierDismissible: false);
|
barrierDismissible: false);
|
||||||
},
|
},
|
||||||
StudentIdRoute.name: (routeData) {
|
StudentIdRoute.name: (routeData) {
|
||||||
return _i8.CustomPage<dynamic>(
|
return _i10.CustomPage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: const _i7.StudentIdScreen(),
|
child: const _i7.StudentIdScreen(),
|
||||||
transitionsBuilder: _i8.TransitionsBuilders.fadeIn,
|
transitionsBuilder: _i10.TransitionsBuilders.fadeIn,
|
||||||
|
opaque: true,
|
||||||
|
barrierDismissible: false);
|
||||||
|
},
|
||||||
|
MapHomeRoute.name: (routeData) {
|
||||||
|
return _i10.CustomPage<dynamic>(
|
||||||
|
routeData: routeData,
|
||||||
|
child: const _i8.MapHomeScreen(),
|
||||||
|
customRouteBuilder: _i12.mapRouteBuilder,
|
||||||
|
opaque: true,
|
||||||
|
barrierDismissible: false);
|
||||||
|
},
|
||||||
|
MapCategoryRoute.name: (routeData) {
|
||||||
|
final args = routeData.argsAs<MapCategoryRouteArgs>();
|
||||||
|
return _i10.CustomPage<dynamic>(
|
||||||
|
routeData: routeData,
|
||||||
|
child: _i9.MapCategoryScreen(category: args.category, key: args.key),
|
||||||
|
customRouteBuilder: _i12.mapRouteBuilder,
|
||||||
opaque: true,
|
opaque: true,
|
||||||
barrierDismissible: false);
|
barrierDismissible: false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<_i8.RouteConfig> get routes => [
|
List<_i10.RouteConfig> get routes => [
|
||||||
_i8.RouteConfig(MainLayout.name, path: '/', children: [
|
_i10.RouteConfig(MainLayout.name, path: '/', children: [
|
||||||
_i8.RouteConfig(HomePageRouter.name,
|
_i10.RouteConfig(HomePageRouter.name,
|
||||||
path: 'home',
|
path: 'home',
|
||||||
parent: MainLayout.name,
|
parent: MainLayout.name,
|
||||||
children: [
|
children: [
|
||||||
_i8.RouteConfig(HomeRoute.name,
|
_i10.RouteConfig(HomeRoute.name,
|
||||||
path: '', parent: HomePageRouter.name),
|
path: '', parent: HomePageRouter.name),
|
||||||
_i8.RouteConfig(StudentIdRoute.name,
|
_i10.RouteConfig(StudentIdRoute.name,
|
||||||
path: 'student-id', parent: HomePageRouter.name)
|
path: 'student-id', parent: HomePageRouter.name)
|
||||||
]),
|
]),
|
||||||
_i8.RouteConfig(MapRoute.name, path: 'map', parent: MainLayout.name),
|
_i10.RouteConfig(MapRoute.name,
|
||||||
_i8.RouteConfig(EventsRoute.name,
|
path: 'map',
|
||||||
|
parent: MainLayout.name,
|
||||||
|
children: [
|
||||||
|
_i10.RouteConfig(MapHomeRoute.name,
|
||||||
|
path: '', parent: MapRoute.name),
|
||||||
|
_i10.RouteConfig(MapCategoryRoute.name,
|
||||||
|
path: 'category/:id', parent: MapRoute.name)
|
||||||
|
]),
|
||||||
|
_i10.RouteConfig(EventsRoute.name,
|
||||||
path: 'events', parent: MainLayout.name),
|
path: 'events', parent: MainLayout.name),
|
||||||
_i8.RouteConfig(InfoRoute.name, path: 'info', parent: MainLayout.name)
|
_i10.RouteConfig(InfoRoute.name,
|
||||||
|
path: 'info', parent: MainLayout.name)
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i1.MainLayout]
|
/// [_i1.MainLayout]
|
||||||
class MainLayout extends _i8.PageRouteInfo<void> {
|
class MainLayout extends _i10.PageRouteInfo<void> {
|
||||||
const MainLayout({List<_i8.PageRouteInfo>? children})
|
const MainLayout({List<_i10.PageRouteInfo>? children})
|
||||||
: super(MainLayout.name, path: '/', initialChildren: children);
|
: super(MainLayout.name, path: '/', initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'MainLayout';
|
static const String name = 'MainLayout';
|
||||||
|
@ -97,8 +127,8 @@ class MainLayout extends _i8.PageRouteInfo<void> {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i2.HomeScreen]
|
/// [_i2.HomeScreen]
|
||||||
class HomePageRouter extends _i8.PageRouteInfo<void> {
|
class HomePageRouter extends _i10.PageRouteInfo<void> {
|
||||||
const HomePageRouter({List<_i8.PageRouteInfo>? children})
|
const HomePageRouter({List<_i10.PageRouteInfo>? children})
|
||||||
: super(HomePageRouter.name, path: 'home', initialChildren: children);
|
: super(HomePageRouter.name, path: 'home', initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'HomePageRouter';
|
static const String name = 'HomePageRouter';
|
||||||
|
@ -106,15 +136,16 @@ class HomePageRouter extends _i8.PageRouteInfo<void> {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i3.MapScreen]
|
/// [_i3.MapScreen]
|
||||||
class MapRoute extends _i8.PageRouteInfo<void> {
|
class MapRoute extends _i10.PageRouteInfo<void> {
|
||||||
const MapRoute() : super(MapRoute.name, path: 'map');
|
const MapRoute({List<_i10.PageRouteInfo>? children})
|
||||||
|
: super(MapRoute.name, path: 'map', initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'MapRoute';
|
static const String name = 'MapRoute';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i4.EventsScreen]
|
/// [_i4.EventsScreen]
|
||||||
class EventsRoute extends _i8.PageRouteInfo<void> {
|
class EventsRoute extends _i10.PageRouteInfo<void> {
|
||||||
const EventsRoute() : super(EventsRoute.name, path: 'events');
|
const EventsRoute() : super(EventsRoute.name, path: 'events');
|
||||||
|
|
||||||
static const String name = 'EventsRoute';
|
static const String name = 'EventsRoute';
|
||||||
|
@ -122,7 +153,7 @@ class EventsRoute extends _i8.PageRouteInfo<void> {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i5.InfoScreen]
|
/// [_i5.InfoScreen]
|
||||||
class InfoRoute extends _i8.PageRouteInfo<void> {
|
class InfoRoute extends _i10.PageRouteInfo<void> {
|
||||||
const InfoRoute() : super(InfoRoute.name, path: 'info');
|
const InfoRoute() : super(InfoRoute.name, path: 'info');
|
||||||
|
|
||||||
static const String name = 'InfoRoute';
|
static const String name = 'InfoRoute';
|
||||||
|
@ -130,7 +161,7 @@ class InfoRoute extends _i8.PageRouteInfo<void> {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i6.HomePageHeader]
|
/// [_i6.HomePageHeader]
|
||||||
class HomeRoute extends _i8.PageRouteInfo<void> {
|
class HomeRoute extends _i10.PageRouteInfo<void> {
|
||||||
const HomeRoute() : super(HomeRoute.name, path: '');
|
const HomeRoute() : super(HomeRoute.name, path: '');
|
||||||
|
|
||||||
static const String name = 'HomeRoute';
|
static const String name = 'HomeRoute';
|
||||||
|
@ -138,8 +169,40 @@ class HomeRoute extends _i8.PageRouteInfo<void> {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i7.StudentIdScreen]
|
/// [_i7.StudentIdScreen]
|
||||||
class StudentIdRoute extends _i8.PageRouteInfo<void> {
|
class StudentIdRoute extends _i10.PageRouteInfo<void> {
|
||||||
const StudentIdRoute() : super(StudentIdRoute.name, path: 'student-id');
|
const StudentIdRoute() : super(StudentIdRoute.name, path: 'student-id');
|
||||||
|
|
||||||
static const String name = 'StudentIdRoute';
|
static const String name = 'StudentIdRoute';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i8.MapHomeScreen]
|
||||||
|
class MapHomeRoute extends _i10.PageRouteInfo<void> {
|
||||||
|
const MapHomeRoute() : super(MapHomeRoute.name, path: '');
|
||||||
|
|
||||||
|
static const String name = 'MapHomeRoute';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i9.MapCategoryScreen]
|
||||||
|
class MapCategoryRoute extends _i10.PageRouteInfo<MapCategoryRouteArgs> {
|
||||||
|
MapCategoryRoute({required _i13.MapCategory category, _i11.Key? key})
|
||||||
|
: super(MapCategoryRoute.name,
|
||||||
|
path: 'category/:id',
|
||||||
|
args: MapCategoryRouteArgs(category: category, key: key));
|
||||||
|
|
||||||
|
static const String name = 'MapCategoryRoute';
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapCategoryRouteArgs {
|
||||||
|
const MapCategoryRouteArgs({required this.category, this.key});
|
||||||
|
|
||||||
|
final _i13.MapCategory category;
|
||||||
|
|
||||||
|
final _i11.Key? key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'MapCategoryRouteArgs{category: $category, key: $key}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import 'dart:async';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:furman_now/src/screens/map/state.dart';
|
||||||
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:transparent_pointer/transparent_pointer.dart';
|
||||||
import 'package:furman_now/src/utils/theme.dart';
|
|
||||||
import 'package:furman_now/src/widgets/map/filter_chip.dart';
|
import 'map_widget.dart';
|
||||||
import 'package:furman_now/src/widgets/map/rotate_compass.dart';
|
|
||||||
import 'package:latlong2/latlong.dart';
|
|
||||||
|
|
||||||
class MapScreen extends StatefulWidget {
|
class MapScreen extends StatefulWidget {
|
||||||
const MapScreen({Key? key}) : super(key: key);
|
const MapScreen({Key? key}) : super(key: key);
|
||||||
|
@ -18,206 +15,34 @@ class MapScreen extends StatefulWidget {
|
||||||
|
|
||||||
class _MapScreenState extends State<MapScreen>
|
class _MapScreenState extends State<MapScreen>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
final MapController _mapController = MapController();
|
|
||||||
|
|
||||||
late final AnimationController _animationController = AnimationController(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
|
|
||||||
late CenterOnLocationUpdate _centerOnLocationUpdate;
|
|
||||||
late StreamController<double?> _centerCurrentLocationStreamController;
|
|
||||||
|
|
||||||
var _rotation = 0.0;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_mapController.mapEventStream.listen((event) {
|
|
||||||
if (event is MapEventRotate) {
|
|
||||||
setState(() {
|
|
||||||
_rotation = _mapController.rotation * (2 * pi) / 360;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_centerOnLocationUpdate = CenterOnLocationUpdate.always;
|
|
||||||
_centerCurrentLocationStreamController = StreamController<double?>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
_animationController.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetRotation() async {
|
|
||||||
// take the shortest rotation path
|
|
||||||
var end = _mapController.rotation > 180 ? 360.0 : 0.0;
|
|
||||||
var animation = Tween<double>(
|
|
||||||
begin: _mapController.rotation,
|
|
||||||
end: end,
|
|
||||||
).animate(CurvedAnimation(
|
|
||||||
parent: _animationController,
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
));
|
|
||||||
|
|
||||||
animationListener() {
|
|
||||||
_mapController.rotate(animation.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
animation.addListener(animationListener);
|
|
||||||
|
|
||||||
await _animationController.forward();
|
|
||||||
|
|
||||||
animation.removeListener(animationListener);
|
|
||||||
_animationController.reset();
|
|
||||||
|
|
||||||
_mapController.rotate(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return ChangeNotifierProvider(
|
||||||
body: Container(
|
create: (context) => MapPageState(vsync: this),
|
||||||
color: const Color(0xffb7acc9),
|
builder: (context, _) => WillPopScope(
|
||||||
child: SafeArea(
|
onWillPop: () async {
|
||||||
top: false,
|
print("Will pop");
|
||||||
child: Stack(
|
return false;
|
||||||
children: [
|
},
|
||||||
FlutterMap(
|
child: Scaffold(
|
||||||
mapController: _mapController,
|
body: Container(
|
||||||
options: MapOptions(
|
color: const Color(0xffb7acc9),
|
||||||
center: LatLng(34.925926, -82.439397),
|
child: SafeArea(
|
||||||
enableMultiFingerGestureRace: true,
|
top: false,
|
||||||
rotationWinGestures: MultiFingerGesture.all,
|
// child: MapWidget(),
|
||||||
pinchZoomThreshold: 0.2,
|
child: Stack(
|
||||||
rotationThreshold: 8,
|
children: const [
|
||||||
zoom: 15,
|
MapWidget(),
|
||||||
minZoom: 12,
|
TransparentPointer(transparent: true, child: AutoRouter()),
|
||||||
maxZoom: 18,
|
|
||||||
plugins: [
|
|
||||||
LocationMarkerPlugin(
|
|
||||||
centerCurrentLocationStream:
|
|
||||||
_centerCurrentLocationStreamController.stream,
|
|
||||||
centerOnLocationUpdate: _centerOnLocationUpdate,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onPositionChanged: (MapPosition position, bool hasGesture) {
|
|
||||||
if (hasGesture) {
|
|
||||||
setState(
|
|
||||||
() => _centerOnLocationUpdate = CenterOnLocationUpdate.never,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
layers: [
|
|
||||||
TileLayerOptions(
|
|
||||||
urlTemplate:
|
|
||||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
||||||
userAgentPackageName: 'edu.furman.now',
|
|
||||||
),
|
|
||||||
LocationMarkerLayerOptions(),
|
|
||||||
],
|
|
||||||
nonRotatedChildren: [
|
|
||||||
AttributionWidget(
|
|
||||||
attributionBuilder: (BuildContext context) {
|
|
||||||
return const ColoredBox(
|
|
||||||
color: Color(0xCCFFFFFF),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(3),
|
|
||||||
child: Text("©️ OpenStreetMap contributors"),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// Rotation reset fab
|
),
|
||||||
Positioned(
|
|
||||||
top: 12,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: SafeArea(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 50,
|
|
||||||
padding: const EdgeInsets.only(left: 10, right: 20),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(60)),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x33000000),
|
|
||||||
blurRadius: 8,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Wrap(
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
children: [
|
|
||||||
SvgPicture.asset("assets/images/bell-tower.svg", color: Theme.of(context).primaryColor, height: 32),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
"Search locations",
|
|
||||||
style: furmanTextStyle(TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.grey.shade500,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// const SizedBox(height: 12),
|
|
||||||
SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 6,
|
|
||||||
children: const [
|
|
||||||
MapFilterChip(icon: Icons.restaurant, text: "Restaurants"),
|
|
||||||
MapFilterChip(icon: Icons.train, text: "Transportation"),
|
|
||||||
MapFilterChip(icon: Icons.school, text: "Campus Buildings"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MapRotateCompass(rotation: _rotation, resetRotation: resetRotation),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 20,
|
|
||||||
bottom: 20,
|
|
||||||
child: FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
// Automatically center the location marker on the map when location updated until user interact with the map.
|
|
||||||
setState(
|
|
||||||
() => _centerOnLocationUpdate = CenterOnLocationUpdate.always,
|
|
||||||
);
|
|
||||||
// Center the location marker on the map and zoom the map to level 18.
|
|
||||||
_centerCurrentLocationStreamController.add(16);
|
|
||||||
},
|
|
||||||
child: const Icon(
|
|
||||||
Icons.my_location,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/state.dart';
|
||||||
|
import 'package:furman_now/src/widgets/map/map_header.dart';
|
||||||
|
import 'package:furman_now/src/widgets/map/rotate_compass.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'map_widget.dart';
|
||||||
|
|
||||||
|
class MapCategoryScreen extends StatelessWidget {
|
||||||
|
final MapCategory category;
|
||||||
|
|
||||||
|
const MapCategoryScreen({
|
||||||
|
required this.category,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<MapPageState>(
|
||||||
|
builder: (context, state, _) => Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: 12,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Hero(
|
||||||
|
tag: "header",
|
||||||
|
child: MapHeader(
|
||||||
|
activeCategory: category.name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// const SizedBox(height: 12),
|
||||||
|
// SingleChildScrollView(
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
|
// scrollDirection: Axis.horizontal,
|
||||||
|
// child: Wrap(
|
||||||
|
// spacing: 6,
|
||||||
|
// children: [
|
||||||
|
// // ...categories.map((category) => MapFilterChip(
|
||||||
|
// // icon: Icons.restaurant,
|
||||||
|
// // text: category.name,
|
||||||
|
// // callback: category.activator
|
||||||
|
// // )),
|
||||||
|
// // MapFilterChip(
|
||||||
|
// // icon: Icons.restaurant,
|
||||||
|
// // text: "Restaurants",
|
||||||
|
// // callback: () => null,
|
||||||
|
// // ),
|
||||||
|
// // MapFilterChip(icon: Icons.train, text: "Transportation"),
|
||||||
|
// // MapFilterChip(icon: Icons.school, text: "Campus Buildings"),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
MapRotateCompass(rotation: state.rotation, resetRotation: state.resetRotation),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Automatically center the location marker on the map when location updated until user interact with the map.
|
||||||
|
state.centerOnLocationUpdate = CenterOnLocationUpdate.always;
|
||||||
|
// Center the location marker on the map and zoom the map to level 16.
|
||||||
|
state.centerCurrentLocationStreamController.add(16);
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.my_location,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/map_widget.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/state.dart';
|
||||||
|
import 'package:furman_now/src/widgets/map/filter_chip.dart';
|
||||||
|
import 'package:furman_now/src/widgets/map/map_header.dart';
|
||||||
|
import 'package:furman_now/src/widgets/map/rotate_compass.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class MapHomeScreen extends StatelessWidget {
|
||||||
|
const MapHomeScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<MapPageState>(
|
||||||
|
builder: (context, state, _) => Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: 12,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Hero(
|
||||||
|
tag: "header",
|
||||||
|
child: MapHeader()
|
||||||
|
),
|
||||||
|
// const SizedBox(height: 12),
|
||||||
|
SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
...state.categories.map((category) => MapFilterChip(
|
||||||
|
icon: Icons.restaurant,
|
||||||
|
text: category.name,
|
||||||
|
callback: category.activator
|
||||||
|
)),
|
||||||
|
// MapFilterChip(
|
||||||
|
// icon: Icons.restaurant,
|
||||||
|
// text: "Restaurants",
|
||||||
|
// callback: () => null,
|
||||||
|
// ),
|
||||||
|
// MapFilterChip(icon: Icons.train, text: "Transportation"),
|
||||||
|
// MapFilterChip(icon: Icons.school, text: "Campus Buildings"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MapRotateCompass(rotation: state.rotation, resetRotation: state.resetRotation),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Automatically center the location marker on the map when location updated until user interact with the map.
|
||||||
|
state.centerOnLocationUpdate = CenterOnLocationUpdate.always;
|
||||||
|
// Center the location marker on the map and zoom the map to level 16.
|
||||||
|
state.centerCurrentLocationStreamController.add(16);
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.my_location,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
||||||
|
import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/state.dart';
|
||||||
|
import 'package:furman_now/src/utils/theme.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class MapWidget extends StatelessWidget {
|
||||||
|
const MapWidget({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<MapPageState>(
|
||||||
|
builder: (context, state, _) => Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
FlutterMap(
|
||||||
|
mapController: state.mapController,
|
||||||
|
options: MapOptions(
|
||||||
|
center: LatLng(34.925926, -82.439397),
|
||||||
|
maxBounds: LatLngBounds(
|
||||||
|
LatLng(34.991937, -82.536251),
|
||||||
|
LatLng(34.813016, -82.328766),
|
||||||
|
),
|
||||||
|
enableMultiFingerGestureRace: true,
|
||||||
|
rotationWinGestures: MultiFingerGesture.all,
|
||||||
|
pinchZoomThreshold: 0.2,
|
||||||
|
rotationThreshold: 8,
|
||||||
|
zoom: 15,
|
||||||
|
minZoom: 12,
|
||||||
|
maxZoom: 18,
|
||||||
|
plugins: [
|
||||||
|
LocationMarkerPlugin(
|
||||||
|
centerCurrentLocationStream:
|
||||||
|
state.centerCurrentLocationStreamController.stream,
|
||||||
|
centerOnLocationUpdate: state.centerOnLocationUpdate,
|
||||||
|
),
|
||||||
|
MarkerClusterPlugin(),
|
||||||
|
],
|
||||||
|
onPositionChanged: (MapPosition position, bool hasGesture) {
|
||||||
|
if (hasGesture) {
|
||||||
|
state.centerOnLocationUpdate = CenterOnLocationUpdate.never;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
layers: [
|
||||||
|
TileLayerOptions(
|
||||||
|
urlTemplate:
|
||||||
|
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
|
userAgentPackageName: 'edu.furman.now',
|
||||||
|
),
|
||||||
|
LocationMarkerLayerOptions(),
|
||||||
|
MarkerClusterLayerOptions(
|
||||||
|
maxClusterRadius: 60,
|
||||||
|
size: const Size(40, 40),
|
||||||
|
fitBoundsOptions: const FitBoundsOptions(
|
||||||
|
padding: EdgeInsets.all(50),
|
||||||
|
),
|
||||||
|
markers: state.markers,
|
||||||
|
polygonOptions: const PolygonOptions(
|
||||||
|
borderColor: Colors.blueAccent,
|
||||||
|
color: Colors.black12,
|
||||||
|
borderStrokeWidth: 3,
|
||||||
|
),
|
||||||
|
builder: (context, markers) {
|
||||||
|
return FloatingActionButton(
|
||||||
|
onPressed: null,
|
||||||
|
child: Text(markers.length.toString()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
nonRotatedChildren: [
|
||||||
|
AttributionWidget(
|
||||||
|
attributionBuilder: (BuildContext context) {
|
||||||
|
return ColoredBox(
|
||||||
|
color: const Color(0xCCFFFFFF),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
child: Text(
|
||||||
|
"©️ OpenStreetMap contributors",
|
||||||
|
style: furmanTextStyle(const TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
|
||||||
|
import 'package:flutter_remix/flutter_remix.dart';
|
||||||
|
import 'package:furman_now/src/services/restaurants/restaurant_service.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class MapCategory {
|
||||||
|
final String name;
|
||||||
|
final IconData icon;
|
||||||
|
final Function activator;
|
||||||
|
|
||||||
|
const MapCategory({
|
||||||
|
required this.name,
|
||||||
|
required this.icon,
|
||||||
|
required this.activator,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapPageState extends ChangeNotifier {
|
||||||
|
MapPageState({
|
||||||
|
required TickerProvider vsync
|
||||||
|
}): _animationController = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
vsync: vsync,
|
||||||
|
) {
|
||||||
|
_initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initState() {
|
||||||
|
mapController.mapEventStream.listen((event) {
|
||||||
|
if (event is MapEventRotate) {
|
||||||
|
rotation = mapController.rotation * (2 * pi) / 360;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
centerOnLocationUpdate = CenterOnLocationUpdate.always;
|
||||||
|
centerCurrentLocationStreamController = StreamController<double?>();
|
||||||
|
}
|
||||||
|
|
||||||
|
final MapController mapController = MapController();
|
||||||
|
final AnimationController _animationController;
|
||||||
|
|
||||||
|
late CenterOnLocationUpdate _centerOnLocationUpdate;
|
||||||
|
CenterOnLocationUpdate get centerOnLocationUpdate => _centerOnLocationUpdate;
|
||||||
|
set centerOnLocationUpdate (CenterOnLocationUpdate value) {
|
||||||
|
_centerOnLocationUpdate = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
late StreamController<double?> centerCurrentLocationStreamController;
|
||||||
|
|
||||||
|
var _rotation = 0.0;
|
||||||
|
double get rotation => _rotation;
|
||||||
|
set rotation (double value) {
|
||||||
|
_rotation = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Marker> _markers = <Marker>[];
|
||||||
|
List<Marker> get markers => _markers;
|
||||||
|
set markers (List<Marker> value) {
|
||||||
|
_markers = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetRotation() async {
|
||||||
|
// take the shortest rotation path
|
||||||
|
var end = mapController.rotation > 180 ? 360.0 : 0.0;
|
||||||
|
var animation = Tween<double>(
|
||||||
|
begin: mapController.rotation,
|
||||||
|
end: end,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _animationController,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
));
|
||||||
|
|
||||||
|
animationListener() {
|
||||||
|
mapController.rotate(animation.value);
|
||||||
|
}
|
||||||
|
animation.addListener(animationListener);
|
||||||
|
|
||||||
|
await _animationController.forward();
|
||||||
|
|
||||||
|
animation.removeListener(animationListener);
|
||||||
|
_animationController.reset();
|
||||||
|
|
||||||
|
mapController.rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final List<MapCategory> categories = [
|
||||||
|
MapCategory(
|
||||||
|
name: "Restaurants",
|
||||||
|
icon: FlutterRemix.restaurant_line,
|
||||||
|
activator: showRestaurants,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Future<void> showRestaurants() async {
|
||||||
|
var restaurants = await RestaurantService.fetchRestaurants();
|
||||||
|
var newMarkers = restaurants.map((restaurant) => Marker(
|
||||||
|
point: restaurant.mapLocation,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
builder: (context) => GestureDetector(
|
||||||
|
onTapDown: (e) => print("tapped"),
|
||||||
|
child: const FlutterLogo()
|
||||||
|
),
|
||||||
|
));
|
||||||
|
markers = newMarkers.toList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TranslucentRoute<T> extends TransitionRoute<T> {
|
||||||
|
final bool _opaque;
|
||||||
|
final Duration _transitionDuration;
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Animation<double> secondaryAnimation,
|
||||||
|
Widget child
|
||||||
|
) _transitionBuilder;
|
||||||
|
final Widget Function(BuildContext) _pageBuilder;
|
||||||
|
|
||||||
|
TranslucentRoute({
|
||||||
|
opaque = true,
|
||||||
|
transitionDuration = const Duration(milliseconds: 300),
|
||||||
|
required Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Animation<double> secondaryAnimation,
|
||||||
|
Widget child
|
||||||
|
) transitionBuilder,
|
||||||
|
required Widget Function(BuildContext) pageBuilder,
|
||||||
|
RouteSettings? settings,
|
||||||
|
}):
|
||||||
|
_opaque = opaque,
|
||||||
|
_transitionDuration = transitionDuration,
|
||||||
|
_transitionBuilder = transitionBuilder,
|
||||||
|
_pageBuilder = pageBuilder,
|
||||||
|
super(settings: settings);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<OverlayEntry> createOverlayEntries() {
|
||||||
|
return <OverlayEntry>[
|
||||||
|
OverlayEntry(
|
||||||
|
builder: (context) => _transitionBuilder(
|
||||||
|
context,
|
||||||
|
animation!,
|
||||||
|
secondaryAnimation!,
|
||||||
|
_pageBuilder(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get opaque => _opaque;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Duration get transitionDuration => _transitionDuration;
|
||||||
|
}
|
|
@ -4,51 +4,56 @@ import 'package:furman_now/src/utils/theme.dart';
|
||||||
class MapFilterChip extends StatelessWidget {
|
class MapFilterChip extends StatelessWidget {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String text;
|
final String text;
|
||||||
|
final Function callback;
|
||||||
|
|
||||||
const MapFilterChip({
|
const MapFilterChip({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.text,
|
required this.text,
|
||||||
|
required this.callback,
|
||||||
Key? key
|
Key? key
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return GestureDetector(
|
||||||
padding: const EdgeInsets.only(
|
onTapDown: (e) => callback(),
|
||||||
top: 6, bottom: 6, left: 8, right: 12),
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
padding: const EdgeInsets.only(
|
||||||
color: Colors.white,
|
top: 6, bottom: 6, left: 8, right: 12),
|
||||||
borderRadius: BorderRadius.circular(100),
|
decoration: BoxDecoration(
|
||||||
boxShadow: const [
|
color: Colors.white,
|
||||||
BoxShadow(
|
borderRadius: BorderRadius.circular(100),
|
||||||
color: Color(0x33000000),
|
boxShadow: const [
|
||||||
blurRadius: 4,
|
BoxShadow(
|
||||||
),
|
color: Color(0x33000000),
|
||||||
],
|
blurRadius: 4,
|
||||||
),
|
|
||||||
child: RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
WidgetSpan(
|
|
||||||
child: Icon(icon,
|
|
||||||
size: 18,
|
|
||||||
color: Colors.grey.shade800),
|
|
||||||
alignment: PlaceholderAlignment.middle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 6)),
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: PlaceholderAlignment.middle,
|
|
||||||
child: Text(
|
|
||||||
text,
|
|
||||||
style: furmanTextStyle(TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.grey.shade800,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
WidgetSpan(
|
||||||
|
child: Icon(icon,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.grey.shade800),
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
),
|
||||||
|
const WidgetSpan(child: SizedBox(width: 6)),
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: furmanTextStyle(TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_remix/flutter_remix.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:furman_now/src/routes/index.gr.dart';
|
||||||
|
import 'package:furman_now/src/screens/map/state.dart';
|
||||||
|
import 'package:furman_now/src/utils/theme.dart';
|
||||||
|
|
||||||
|
class MapHeader extends StatelessWidget {
|
||||||
|
final String? activeCategory;
|
||||||
|
|
||||||
|
const MapHeader({
|
||||||
|
this.activeCategory,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
padding: const EdgeInsets.only(left: 10, right: 20),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(60)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color(0x33000000),
|
||||||
|
blurRadius: 8,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
crossFadeState: activeCategory == null ? CrossFadeState.showFirst : CrossFadeState.showSecond,
|
||||||
|
firstChild: GestureDetector(
|
||||||
|
onTapDown: (e) => context.router.push(MapCategoryRoute(
|
||||||
|
category: MapCategory(
|
||||||
|
name: "Restaurants",
|
||||||
|
icon: FlutterRemix.restaurant_line,
|
||||||
|
activator: () => null,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
child: Wrap(
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset("assets/images/bell-tower.svg", color: Theme.of(context).primaryColor, height: 32),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
"Search locations",
|
||||||
|
style: furmanTextStyle(TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey.shade500,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secondChild: Wrap(
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTapDown: (e) => context.router.navigateBack(),
|
||||||
|
child: Icon(FlutterRemix.arrow_left_line, size: 28, color: Colors.grey.shade800,),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
if (activeCategory != null)
|
||||||
|
Text(
|
||||||
|
activeCategory!,
|
||||||
|
style: furmanTextStyle(TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -812,6 +812,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
transparent_pointer:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: transparent_pointer
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -53,6 +53,7 @@ dependencies:
|
||||||
weather: ^2.0.1
|
weather: ^2.0.1
|
||||||
geolocator: ^9.0.1
|
geolocator: ^9.0.1
|
||||||
flutter_map_marker_cluster: ^0.5.4
|
flutter_map_marker_cluster: ^0.5.4
|
||||||
|
transparent_pointer: ^1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in New Issue