void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await FirebaseMessageApi().initNotifications();
final GoogleMapsFlutterPlatform mapsImplementation =
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
mapsImplementation.useAndroidViewSurface = true;
mapsImplementation.initializeWithRenderer(AndroidMapRenderer.latest);
}
runApp(MaterialApp(
home: RootScreen(),
));
}
main.dart
class RootScreen extends StatefulWidget {
const RootScreen({super.key});
@override
State<RootScreen> createState() => _RootScreenState();
}
class _RootScreenState extends State<RootScreen> with TickerProviderStateMixin{
final PageController _controller = PageController();
...
@override
Widget build(BuildContext context) {
print('RootScreen build');
return ChangeNotifierProvider(
create: (context) => GoogleMapModel(),
child: Scaffold(
body: PageView(
controller: _controller,
onPageChanged: onItemTapped,
children: widgetOptions,
),
bottomNavigationBar: SizedBox(
height: 70,
child: renderBottomNavigationBar()),
extendBody: false,
extendBodyBehindAppBar: true,
),
);
}
}
part of RootScreen.dart
In some widget, it passes marker item (cluster) and circles. So I used provider to pass the data globally. The problem is that I need to pass context when creating circles.
class GoogleMapModel with ChangeNotifier {
final dio = Dio();
late RestClient client = RestClient(dio);
List<Circle> _circleItems = [];
List<ClusterData> _markerItems = [];
List<ScrollableSheetData> _sheetItems = [];
String _sheetTitle = "";
String _bottomSheetTitle = "";
List<Circle> get circleItems => _circleItems;
List<ClusterData> get markerItems => _markerItems;
List<ScrollableSheetData> get sheetItems => _sheetItems;
String get sheetTitle => _sheetTitle;
String get bottomSheetTitle => _bottomSheetTitle;
void RemoveItems() {
_circleItems.clear();
_markerItems.clear();
_sheetItems.clear();
notifyListeners();
}
void EarthQuakeItems() async {
_circleItems.clear();
_markerItems.clear();
_sheetItems.clear();
try {
List<EarthQuake> value = await client.getEarthQuakeRecent(); // Retrofit rest api
if (value.isNotEmpty) {
for (int i = 0; i < value.length; i++) {
_circleItems.add(Circle(
circleId: CircleId(value[i].id.toString()),
center: LatLng(value[i].latitude, value[i].longitude),
fillColor: Colors.blue.withOpacity(0.5),
radius: value[i].magnitude * 10000,
strokeColor: Colors.blueAccent,
strokeWidth: 1,
onTap: () {
BottomSheets.showItemBottomSheet(context, value[i].id.toString());
},
consumeTapEvents: true,
));
}
}
} catch (error) {
// TODO
}
notifyListeners();
}
class BottomSheets {
static void showItemBottomSheet(BuildContext context, String data){
showBottomSheet(
context: context,
builder: (context) {
BottomSheets are class something like this. It requires context, but I cannot figure how to pass context in GoogleMapModel class. Is there any way that I can pass context? Or is there any good way to show a container when circle is tap? I just have to show a container so showBottomSheet is not necessary.
I tried GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
this thing. but it was no use. Or maybe i called it wrong
+) How I call method
class EQ_Info extends StatefulWidget {
const EQ_Info({super.key});
@override
State<EQ_Info> createState() => _EQ_InfoState();
}
class _EQ_InfoState extends State<EQ_Info> {
List<Circle> circleItems = [];
List<ClusterData> markerItems = [];
...
@override
Widget build(BuildContext context) {
return SafeArea(
child: Stack(
children: [
Google_Map(
circleItems: context.watch<GoogleMapModel>().circleItems,
markerItems: context.watch<GoogleMapModel>().markerItems,
mode: GoogleMapMode.shelter,
),
const CustomCategory(),
CustomScrollableSheet(),
],
),
);
}
}
class CustomCategory extends StatefulWidget {
const CustomCategory({super.key});
@override
State<CustomCategory> createState() => _CustomCategoryState();
}
class _CustomCategoryState extends State<CustomCategory> {
int? _selectedIndex;
List<Widget> choiceChips() {
List<Widget> chips = [];
for (int i = 0; i < _choiceChipsList.length; i++) {
Widget item = Padding(
padding: const EdgeInsets.only(left: 2.5, right: 2.5),
child: ChoiceChip(
avatar: CircleAvatar(
child: Icon(_choiceChipsList[i].iconData),
),
label: Text(_choiceChipsList[i].label),
backgroundColor: Colors.white,
selected: _selectedIndex == i,
selectedColor: Colors.deepOrange,
onSelected: (bool selected) {
setState(() {
_selectedIndex = selected ? i : null;
_handleSelection(_selectedIndex);
});
},
),
);
chips.add(item);
}
return chips;
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
padding: const EdgeInsets.only(top: 5, left: 5, right: 5),
child: Row(
children: choiceChips(),
),
),
);
}
void _handleSelection(int? selectedIndex) {
switch(_selectedIndex){
case null:
case 2:
case 3:
context.read<GoogleMapModel>().RemoveItems();
break;
case 0:
context.read<GoogleMapModel>().EarthQuakeItems();
break;
case 1:
context.read<GoogleMapModel>().ShelterItems();
break;
default:
context.read<GoogleMapModel>().RemoveItems();
break;
}
}
..
The function needs to be called above of google map widget
Using navigator key should do the work, here is how to use it:
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// ...
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
navigatorKey: navigatorKey,
// ...
)
Then you need to pass it to your RootScreen and then pass to GoogleMapModel
// ...
return ChangeNotifierProvider(
create: (context) => GoogleMapModel(navigatorKey),
// ...
in GoogleMapModel you can store it and then use it like this
class GoogleMapModel with ChangeNotifier {
GoogleMapModel(this.navigatorKey);
final GlobalKey<NavigatorState> navigatorKey;
void somewhereYouNeedContext() {
final context = navigatorKey.currentContext;
}
}
However, wouldn't it be easier to just pass onTap
as an argument in a EarthQuakeItems
method? It'd work only if you call this from a place that has context like Widget. You didn't specify where you call EarthQuakeItems, but I suppose it is somewhere like initState
The implementation would look something like this:
void EarthQuakeItems(VoidCallback onTap) async {
// ...
_circleItems.add(Circle(
onTap: onTap,
))
// ...
}