Skip to content

Instantly share code, notes, and snippets.

@aloisdeniel
Last active January 21, 2026 23:03
Show Gist options
  • Select an option

  • Save aloisdeniel/ee8376d7444afe1ba98a13727d4edeb8 to your computer and use it in GitHub Desktop.

Select an option

Save aloisdeniel/ee8376d7444afe1ba98a13727d4edeb8 to your computer and use it in GitHub Desktop.
Flutter Codegen plugin for Figma / Components
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
abstract class Colors {
static const white = Color(0xFFFFFFFF);
static const blueLight = Color(0xFFECEFFF);
static const blueMedium = Color(0xFF4463FF);
static const blueMediumDark = Color(0xFF314DD5);
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 32,
children: [
Button(title: 'Continue', onPressed: () {}),
// Widget preview instances
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 4,
children: [
for (final style in ButtonStyleVariant.values)
for (final state in ButtonStateVariant.values)
ButtonProperties(
data: ButtonData(
title: 'Preview',
style: style,
state: state,
),
child: const ButtonLayout(),
),
],
),
],
),
),
);
}
}
// This widget renders the visuals from the component properties
class ButtonLayout extends StatelessWidget {
const ButtonLayout({super.key});
@override
Widget build(BuildContext context) {
final properties = ButtonProperties.of(context);
final decoration = switch (properties.style) {
ButtonStyleVariant.filled => BoxDecoration(
color: switch (properties.state) {
ButtonStateVariant.hover => Colors.blueMediumDark,
_ => Colors.blueMedium,
},
borderRadius: BorderRadius.circular(100),
),
ButtonStyleVariant.border => BoxDecoration(
border: Border.all(
color: switch (properties.state) {
ButtonStateVariant.hover => Colors.blueMediumDark,
_ => Colors.blueMedium,
},
width: 2,
),
borderRadius: BorderRadius.circular(100),
),
ButtonStyleVariant.text => BoxDecoration(
color: switch (properties.state) {
ButtonStateVariant.hover => Colors.blueLight,
_ => Colors.blueLight.withValues(alpha: 0),
},
),
};
return AnimatedContainer(
duration: const Duration(milliseconds: 250),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: decoration,
child: Text(
properties.title,
style: TextStyle(
color: switch (properties.style) {
ButtonStyleVariant.filled => Colors.blueLight,
_ => Colors.blueMediumDark,
},
decoration: TextDecoration.none,
fontSize: 12,
),
),
);
}
}
/// This widgets adds the behaviours to create to right properties
/// for the layout.
class Button extends StatefulWidget {
const Button({
super.key,
required this.title,
this.style = ButtonStyleVariant.filled,
this.onPressed,
});
final String title;
final ButtonStyleVariant style;
final VoidCallback? onPressed;
@override
State<Button> createState() => _ButtonState();
}
class _ButtonState extends State<Button> {
var _hovering = false;
var _pressed = false;
void handleAnyTapDown(TapDownDetails details) {
if (!_pressed) {
setState(() {
_pressed = true;
});
}
}
void handleAnyTapUpOrCancel() {
if (_pressed) {
setState(() {
_pressed = false;
});
}
}
void handleMouseEnter(PointerEnterEvent event) {
if (!_hovering) {
setState(() {
_hovering = true;
});
}
}
void handleMouseExit(PointerExitEvent event) {
if (_hovering) {
setState(() {
_hovering = false;
});
}
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: handleMouseEnter,
onExit: handleMouseExit,
child: GestureDetector(
onTapDown: handleAnyTapDown,
onTapUp: (_) => handleAnyTapUpOrCancel(),
onTapCancel: handleAnyTapUpOrCancel,
onTap: widget.onPressed,
child: ButtonProperties.merge(
context,
title: widget.title,
style: widget.style,
state: switch ((_pressed, _hovering)) {
(false, false) => ButtonStateVariant.default_,
_ => ButtonStateVariant.hover,
},
child: ButtonLayout(),
),
),
);
}
}
// The code below is generated with the Flutter Codegen plugin for Figma
enum ButtonStateVariant { default_, hover }
enum ButtonStyleVariant { filled, border, text }
class ButtonData {
const ButtonData({
this.title = 'Click me',
this.state = ButtonStateVariant.default_,
this.style = ButtonStyleVariant.filled,
});
final String title;
final ButtonStateVariant state;
final ButtonStyleVariant style;
@override
int get hashCode {
return Object.hashAll([title, state, style]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ButtonData &&
other.title == title &&
other.state == state &&
other.style == style;
}
@override
String toString() {
return 'ButtonData('
'title: $title, '
'state: $state, '
'style: $style'
')';
}
ButtonData copyWith({
String? title,
ButtonStateVariant? state,
ButtonStyleVariant? style,
}) {
return ButtonData(
title: title ?? this.title,
state: state ?? this.state,
style: style ?? this.style,
);
}
}
class ButtonProperties extends InheritedWidget {
const ButtonProperties({super.key, required this.data, required super.child});
factory ButtonProperties.merge(
BuildContext context, {
Key? key,
required Widget child,
String? title,
ButtonStateVariant? state,
ButtonStyleVariant? style,
}) {
var properties = ButtonProperties.maybeOf(context) ?? const ButtonData();
return ButtonProperties(
key: key,
data: ButtonData(
title: title ?? properties.title,
state: state ?? properties.state,
style: style ?? properties.style,
),
child: child,
);
}
final ButtonData data;
static ButtonData? maybeOf(BuildContext context) {
final result = context
.dependOnInheritedWidgetOfExactType<ButtonProperties>();
return result?.data;
}
static ButtonData of(BuildContext context) {
final result = maybeOf(context);
assert(result != null, 'No ButtonProperties found in context');
return result!;
}
@override
bool updateShouldNotify(ButtonProperties oldWidget) => data != oldWidget.data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment