Skip to content

Instantly share code, notes, and snippets.

@CristhianLara1996
Created November 24, 2022 22:05
Show Gist options
  • Save CristhianLara1996/2e174e6a58c6f46c5e84a937baa6b3e7 to your computer and use it in GitHub Desktop.
Save CristhianLara1996/2e174e6a58c6f46c5e84a937baa6b3e7 to your computer and use it in GitHub Desktop.
Convert Widget To Bytes
Future<void> _assignWidgetToMarker(
double size,
Model item,
double iconSizeMarker,
State state,
) async {
return WidgetToByte(
size: size,
markerWidget: ContainerWithShadow(
child: SvgPicture.asset(
'assets/icons/map/marker.svg',
height: size,
color: item.status == 'enabled'
? Colors.red
: Colors.blue,
),
widget: SvgPicture.asset(
item.status == 'enabled'
? iconPending
: iconCompleted,
height: iconSizeMarker,
width: iconSizeMarker,
),
height: size,
width: size,
text: '1',
),
callback: (Uint8List image) {
_markers.add(
Marker(
consumeTapEvents: true,
markerId: MarkerId(item.uuid),
position: LatLng(item.latitude, item.longitude),
onTap: () async {
await moveCamera(LatLng(item.latitude, item.longitude))!
.then((value) {
setState(() {
infoWindowShown = true;
});
});
},
icon: BitmapDescriptor.fromBytes(image),
anchor: const Offset(0.5, 0.5),
),
);
setState(() {});
},
).generate(context);
}
///////////////////////
class WidgetToByte {
final Function(Uint8List) callback;
final Widget markerWidget;
final double size;
WidgetToByte({
required this.markerWidget,
required this.callback,
required this.size,
});
void generate(BuildContext context) {
WidgetsBinding.instance
.addPostFrameCallback((_) => afterFirstLayout(context));
}
void afterFirstLayout(BuildContext context) {
addOverlay(context);
}
void addOverlay(BuildContext context) {
OverlayState? overlayState = Overlay.of(context);
OverlayEntry entry = OverlayEntry(
builder: (context) {
return _MarkerHelper(
size: size,
markerWidgets: markerWidget,
callback: (Uint8List bitmapList) {
callback.call(bitmapList);
},
);
},
maintainState: true,
);
overlayState?.insert(entry);
}
}
/// Maps are embeding GoogleMap library for Andorid/iOS into flutter.
///
/// These native libraries accept BitmapDescriptor for marker, which means that for custom markers
/// you need to draw view to bitmap and then send that to BitmapDescriptor.
///
/// Because of that Flutter also cannot accept Widget for marker, but you need draw it to bitmap and
/// that's what this widget does:
///
/// 1) It draws marker widget to tree
/// 2) After painted access the repaint boundary with global key and converts it to uInt8List
/// 3) Returns set of Uint8List (bitmaps) through callback
class _MarkerHelper extends StatefulWidget {
final Widget markerWidgets;
final Function(Uint8List) callback;
final double size;
const _MarkerHelper({
required this.markerWidgets,
required this.callback,
required this.size,
Key? key,
}) : super(key: key);
@override
_MarkerHelperState createState() => _MarkerHelperState();
}
class _MarkerHelperState extends State<_MarkerHelper> with AfterLayoutMixin {
GlobalKey globalKeys = GlobalKey();
@override
void afterFirstLayout(BuildContext context) {
_getBitmaps(context, widget.size).then((list) {
widget.callback(list);
});
}
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(MediaQuery.of(context).size.width, 0),
child: Material(
type: MaterialType.transparency,
child: Stack(
children: <Widget>[
SizedBox(
height: widget.size,
width: widget.size,
child: RepaintBoundary(
key: globalKeys,
child: widget.markerWidgets,
),
),
],
),
),
);
}
Future<Uint8List> _getBitmaps(BuildContext context, double size) async {
var futures = await _getUint8List(globalKeys, size);
return futures;
}
Future<Uint8List> _getUint8List(GlobalKey markerKey, double size) async {
return Future.delayed(const Duration(milliseconds: 20), () async {
RenderRepaintBoundary boundary =
markerKey.currentContext?.findRenderObject() as RenderRepaintBoundary;
boundary.layout(
BoxConstraints(
maxWidth: size,
maxHeight: size,
minWidth: size,
minHeight: size,
),
parentUsesSize: true,
);
var image = await boundary.toImage(pixelRatio: 0.55);
ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return byteData!.buffer.asUint8List();
});
}
}
/// AfterLayoutMixin
mixin AfterLayoutMixin<T extends StatefulWidget> on State<T> {
@override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => afterFirstLayout(context));
}
void afterFirstLayout(BuildContext context);
}
/////////////////////////////
class ContainerWithShadow extends StatelessWidget {
const ContainerWithShadow({
required this.child,
Key? key,
this.height = 20,
this.width = 20,
this.text = '',
this.widget,
this.withShadow = false,
}) : super(key: key);
final double height;
final double width;
final String text;
final Widget? widget;
final Widget child;
final bool withShadow;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
boxShadow: [
if (withShadow)
const BoxShadow(
blurRadius: 2,
spreadRadius: -4,
color: TulColors.disabledColor,
offset: Offset(0, -1),
),
],
),
child: child,
),
Positioned(
top: height / 4,
right: -10,
child: Container(
alignment: Alignment.topCenter,
height: height,
width: width,
child: TulText.fontSizeCustom(
label: text,
fontWeight: FontWeight.bold,
fontSize: 45,
),
),
),
if (widget != null)
Positioned(
top: 0,
right: -5,
child: widget!,
),
],
),
);
}
}
class Triangle extends CustomPainter {
final Color? color;
Triangle({this.color});
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = color!
..strokeWidth = 10
..style = PaintingStyle.fill;
var path = Path();
path.moveTo(0.0, 0.0);
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width, 0.0);
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(Triangle oldDelegate) => false;
@override
bool shouldRebuildSemantics(Triangle oldDelegate) => false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment