HomeOur Team
Custom Marker và Info Window trong GoogleMap Flutter - Tập 2

Custom Marker và Info Window trong GoogleMap Flutter - Tập 2

By dong.dam
Published in Solutions
December 19, 2022
1 min read

Hế lô các bẹn đã đến với phần 2 Custom marker và Info window trong Flutter. Tại phần trước, mình đã hướng dẫn các bạn cách để convert ảnh sang bitmap để sử dụng thay thế marker trong Google map. Hôm nay mình sẽ hướng dẫn các bạn cách để Group Marker như thế này nhé:

group.png

Lưu ý: Mình sẽ comment giải thích bên trong code luôn nhé

1. Thêm thư viện này vào nhé:
  • Cluster manager: Đây là thư viện giúp các bạn dễ dàng Group marker hơn, việc mình cần làm chỉ là vẽ cái vòng tròn bên ngoài thôi nhé
2. Vẽ cái vòng tròn group nào
  • Các bạn thêm đoạn code này vào trong file Bitmap convert mà chúng ta đã vẽ lại Marker tại tập trước là xong được một nửa rồi đấy
static Future<BitmapDescriptor> _bitmapGroup(
String text,
int size,
) async {
/// Đoạn này là mình define Canvas để chuẩn bị vẽ và khởi tạo loại màu để fill lên vòng tròn
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
final Paint paint1 = Paint()..color = Colors.orangeAccent;
final Paint paint2 = Paint()..color = Colors.white;
/// Chỗ này là hàm có sẵn của Canvas giúp vẽ hình tròn
/// Ở đây mình vẽ 3 hình tròn để lấy được border bên ngoài hình tròn
/// Mình chỉ việc đưa vào:
/// Offset: Tọa độ (dx,dy)
/// size/2.0: bán kính hình tròn
/// paint1: màu sẽ fill lên hình tròn đó
canvas.drawCircle(Offset(size / 2, size / 2), size / 2.0, paint1);
canvas.drawCircle(Offset(size / 2, size / 2), size / 2.0, paint2);
canvas.drawCircle(Offset(size / 2, size / 2), size / 2.2, paint1);
TextPainter painter = TextPainter(textDirection: TextDirection.ltr);
painter.text = TextSpan(
text: text,
style: TextStyle(
fontSize: size / 3,
color: Colors.white,
fontWeight: FontWeight.normal),
);
painter.layout();
/// Xong rồi gán vào hàm paint để vẽ hình tròn với text ở giữa
painter.paint(
canvas,
Offset(size / 2 - painter.width / 2, size / 2 - painter.height / 2),
);
/// Chỗ này là convert sang dạng ảnh giống như hàm convert svg tại tập 1 thôi
ui.Image img = await pictureRecorder.endRecording().toImage(size, size);
ByteData? data = await img.toByteData(format: ui.ImageByteFormat.png);
return BitmapDescriptor.fromBytes(data!.buffer.asUint8List());
}
3. Qua bên Model Place mà hôm trước tạo nào
  • Các bạn để Model extend ClusterItem nhé, thằng này sẽ phải override lại location để lấy vị trí cho Group marker
class PlaceModel with ClusterItem {
final String id;
final String name;
final LatLng latLng;
PlaceModel(
this.id, {
required this.name,
required this.latLng,
});
@override
LatLng get location => latLng;
}
4. Giờ thì qua View xử lý phần còn lại thôi
  • Tạo 1 hàm để group _initClusterManager
ClusterManager _initClusterManager() {
return ClusterManager<PlaceModel>(
_mapController.places, // Đưa vào List Place
_updateMarkers, // Update lại Marker
markerBuilder: _markerBuilder, // Build marker khi có 1 phần tử hoặc Group nhiều phần tử
);
}
void _updateMarkers(Set<Marker> markers) {
setState(() {
_mapController.markers = markers;
});
}
  • _markerBuilder sẽ là hàm để xác định Marker có hình dạng như nào ( Hình tròn hay là cái cọc cô đơn trong mùa đông giá lạnh)
Future<Marker> Function(Cluster<PlaceModel>) get _markerBuilder =>
(cluster) async {
// Chỗ này mình mặc dù library trả về `items` là List
// nhưng chỉ có 1 phần tử nên mình lấy phần tử đầu tiên luôn
final item = cluster.items.toList().first;
// Chỗ này là gán Marker bình thường thôi
return Marker(
markerId: MarkerId(cluster.getId()),
position: cluster.location,
icon: await BitmapConvert.convertMarkerBitmap(
context,
120,
text: cluster.isMultiple ? cluster.count.toString() : null,
id: item.id,
),
infoWindow: InfoWindow(title: item.name),
);
};
// Đảm nhiệm check nếu Cluster truyền vào từ bên kia có text
// Thì sẽ Group không thì để Marker custom như tập 1
static Future<BitmapDescriptor> convertMarkerBitmap(
BuildContext context,
int size, {
String? text,
required int id,
}) async {
if (text != null) {
return await _bitmapGroup(text, size);
} else {
return await _bitmapSvg(context, id);
}
}
  • Ồ kế qua bước cuối cùng nào
  • Trong hàm initState gán biến global ClusterManager với hàm _initClusterManager đã tạo ở trên
late ClusterManager _manager;
@override
void initState() { super.initState();
_mapController.loadPlace();
_manager = _initClusterManager();
}
  • Code như này trong Widget GoogleMap nhé, mình sẽ giải thích ở comment ( nhớ đọc nhé)
GoogleMap(
markers: _mapController.markers,
initialCameraPosition: const CameraPosition(
target: LatLng(35.226642, 136.735311),
zoom: 15.0,
),
      // Chỗ này là hàm tạo Map, mình sẽ set cluster Id tương ứng với mapId
onMapCreated: (GoogleMapController controller) {
_manager.setMapId(controller.mapId);
},
     // Hàm này là khi camera thay đổi thì sẽ vẽ lại những view Group tương ứng
onCameraMove: (position) {
_manager.onCameraMove(position);
},
     // Hàm này update lại Map đã được vẽ lại khi mình dừng di chuyển camera
onCameraIdle: () {
_manager.updateMap();
},
);
  • Ô kê vậy là đã xong, các bạn hãy run để xem thành quả nhé

Share

dong.dam

dong.dam

Developer

Expertise

Related Posts

Custom Marker và Info Window trong GoogleMap Flutter - Tập cuối
Solutions
Custom Marker và Info Window trong GoogleMap Flutter - Tập cuối
December 21, 2022
2 min
© 2023, All Rights Reserved.
Powered By

Quick Links

HomeOur Team

Social Media