Flutter Google Maps自定义信息窗口多页面左右滑动实现问询
为Google Maps自定义信息窗口添加多页面滑动功能
问题描述
我们正在开发一款美食园区定位应用,需求为点击Google Maps标记后,弹出可左右滑动切换多页面的自定义信息窗口。目前已实现自定义标记,但不知如何为CustomInfoWindow添加多页面滑动功能,现有代码如下:
import 'package:custom_info_window/custom_info_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; const LatLng customLocation = LatLng(14.852885352588201, 120.81614049713285); class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { late GoogleMapController _mapController; final Map<String, Marker> _markers = {}; final _customInfoWindowController = CustomInfoWindowController(); @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ GoogleMap( initialCameraPosition: const CameraPosition(target: customLocation, zoom: 11.5), onMapCreated: (controller) { _mapController = controller; _customInfoWindowController.googleMapController = controller; addMarker5('test5', const LatLng(14.896344082681443, 120.77674251390367)); }, onTap: (location) { _customInfoWindowController.hideInfoWindow!(); }, onCameraMove: (position) { _customInfoWindowController.onCameraMove!(); }, markers: _markers.values.toSet(), ), CustomInfoWindow( controller: _customInfoWindowController, height: 245, width: 220, offset: 50, ) ], ), ); } addMarker5(String markerid4, LatLng location) async { var markerIcon = await BitmapDescriptor.fromAssetImage( const ImageConfiguration(), 'assets/images/sanmarcosfoodpark.png'); var marker = Marker( markerId: MarkerId(markerid4), position: location, icon: markerIcon, onTap: () { _customInfoWindowController.addInfoWindow!( Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), ), child: Column( children: [ Image.asset( 'assets/images/sanmarcoswindow.jpg', fit: BoxFit.cover, ), const Text( 'San Marcos Foodpark', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), const Text( 'Address:', textAlign: TextAlign.right, style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), const Padding( padding: EdgeInsets.only(left: 8.0, right: 8.0), child: Text( '13, R-9, Calumpit, Bulacan', style: TextStyle(fontSize: 11), ), ), const SizedBox( height: 5.0, ), const Text('Contact no: ', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), const Text('', style: TextStyle( fontSize: 11, )), ], ), ), location, ); }); _markers[markerid4] = marker; setState(() {}); } }
解决方案
通过PageView组件可以轻松实现自定义信息窗口的多页面滑动功能,以下是修改后的完整代码:
import 'package:custom_info_window/custom_info_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; const LatLng customLocation = LatLng(14.852885352588201, 120.81614049713285); class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { late GoogleMapController _mapController; final Map<String, Marker> _markers = {}; final _customInfoWindowController = CustomInfoWindowController(); // 新增PageController管理页面切换 final PageController _pageController = PageController(initialPage: 0); // 跟踪当前页面索引,用于指示器 int _currentPage = 0; @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ GoogleMap( initialCameraPosition: const CameraPosition(target: customLocation, zoom: 11.5), onMapCreated: (controller) { _mapController = controller; _customInfoWindowController.googleMapController = controller; addMarker5('test5', const LatLng(14.896344082681443, 120.77674251390367)); }, onTap: (location) { _customInfoWindowController.hideInfoWindow!(); }, onCameraMove: (position) { _customInfoWindowController.onCameraMove!(); }, markers: _markers.values.toSet(), ), CustomInfoWindow( controller: _customInfoWindowController, height: 245, width: 220, offset: 50, ) ], ), ); } addMarker5(String markerid4, LatLng location) async { var markerIcon = await BitmapDescriptor.fromAssetImage( const ImageConfiguration(), 'assets/images/sanmarcosfoodpark.png'); var marker = Marker( markerId: MarkerId(markerid4), position: location, icon: markerIcon, onTap: () { _customInfoWindowController.addInfoWindow!( Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), ), child: Column( children: [ // 用PageView替换原有的单一内容,实现多页面滑动 Expanded( child: PageView( controller: _pageController, onPageChanged: (index) { setState(() { _currentPage = index; }); }, children: [ // 页面1:基础信息 _buildInfoPage(), // 页面2:餐厅评分与评价 _buildRatingPage(), // 页面3:菜单预览 _buildMenuPage(), ], ), ), // 添加页面指示器 Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (index) { return Container( width: 8, height: 8, margin: const EdgeInsets.symmetric(horizontal: 3), decoration: BoxDecoration( shape: BoxShape.circle, color: _currentPage == index ? Colors.orange : Colors.grey.shade300, ), ); }), ), ) ], ), ), location, ); }); _markers[markerid4] = marker; setState(() {}); } // 构建基础信息页面 Widget _buildInfoPage() { return Column( children: [ Image.asset( 'assets/images/sanmarcoswindow.jpg', fit: BoxFit.cover, height: 120, width: double.infinity, ), const SizedBox(height: 8), const Text( 'San Marcos Foodpark', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), const SizedBox(height: 4), const Text( 'Address:', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold), ), const Padding( padding: EdgeInsets.symmetric(horizontal: 8.0), child: Text( '13, R-9, Calumpit, Bulacan', style: TextStyle(fontSize: 11), textAlign: TextAlign.center, ), ), const SizedBox(height: 4), const Text('Contact no: ', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), const Text('(044) 123-4567', style: TextStyle(fontSize: 11)), ], ); } // 构建评分页面 Widget _buildRatingPage() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( '用户评分', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), const SizedBox(height: 10), RatingBar.builder( initialRating: 4.5, minRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 20, itemPadding: const EdgeInsets.symmetric(horizontal: 4.0), itemBuilder: (context, _) => const Icon( Icons.star, color: Colors.orange, ), onRatingUpdate: (rating) {}, ), const SizedBox(height: 10), const Text( '基于24条用户评价', style: TextStyle(fontSize: 11, color: Colors.grey), ), const SizedBox(height: 8), const Padding( padding: EdgeInsets.symmetric(horizontal: 16.0), child: Text( '食物新鲜美味,园区环境干净舒适,推荐尝试烧烤套餐!', style: TextStyle(fontSize: 11), textAlign: TextAlign.center, ), ) ], ); } // 构建菜单预览页面 Widget _buildMenuPage() { return Column( children: [ const Text( '热门菜品', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Expanded( child: ListView( padding: const EdgeInsets.symmetric(horizontal: 8.0), children: const [ ListTile( contentPadding: EdgeInsets.zero, leading: Icon(Icons.fastfood, size: 18), title: Text('烧烤拼盘', style: TextStyle(fontSize: 12)), trailing: Text('₱299', style: TextStyle(fontSize: 12)), ), Divider(height: 10), ListTile( contentPadding: EdgeInsets.zero, leading: Icon(Icons.local_drink, size: 18), title: Text('鲜榨芒果汁', style: TextStyle(fontSize: 12)), trailing: Text('₱49', style: TextStyle(fontSize: 12)), ), Divider(height: 10), ListTile( contentPadding: EdgeInsets.zero, leading: Icon(Icons.bakery_dining, size: 18), title: Text('芝士披萨', style: TextStyle(fontSize: 12)), trailing: Text('₱199', style: TextStyle(fontSize: 12)), ), ], ), ) ], ); } }
关键修改说明
- 新增
PageController用于控制页面切换,并在组件销毁时释放资源 - 将CustomInfoWindow的内容替换为
PageView,包含多个自定义页面组件 - 添加页面指示器,帮助用户直观了解当前页面位置
- 将原有内容拆分为独立的页面构建方法,代码结构更清晰
- 适配每个页面的布局,确保在CustomInfoWindow的尺寸内正常显示
内容的提问来源于stack exchange,提问作者foodpark app
相关产品推荐
相关产品推荐

