Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: MyHomePage()),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _check1 = false;
bool _check2 = true;
bool _check3 = false;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
MyCustomButton(label: "Custom Btn 1", onPressed: () => print("Pressed")),
MyCustomButton(label: "Custom Btn 2", onPressed: () => print("Pressed")),
MyCustomButton(label: "Custom Btn 3", onPressed: () => print("Pressed")),
],
),
SizedBox(height: 60),
MyCustomCheckbox(label: "Check 1", value: _check1, onChanged: (v) => setState(() => _check1 = v)),
SizedBox(height: 8),
MyCustomCheckbox(label: "Check 2", value: _check2, onChanged: (v) => setState(() => _check2 = v)),
SizedBox(height: 8),
MyCustomCheckbox(label: "Check 3", value: _check3, onChanged: (v) => setState(() => _check3 = v)),
],
),
);
}
}
class MyCustomButton extends StatefulWidget {
const MyCustomButton({Key? key, required this.onPressed, required this.label}) : super(key: key);
final VoidCallback onPressed;
final String label;
@override
State<MyCustomButton> createState() => _MyCustomButtonState();
}
class _MyCustomButtonState extends State<MyCustomButton> {
bool _isHovered = false;
bool _isFocused = false;
FocusNode _focusNode = FocusNode();
@override
Widget build(BuildContext context) {
// Change visuals based on focus/hover state
Color outlineColor = _isFocused ? Colors.black : Colors.transparent;
Color bgColor = _isHovered ? Colors.blue.shade100 : Colors.white;
return GestureDetector(
onTap: _handlePressed,
child: FocusableActionDetector(
// Hook up the built-in `ActivateIntent` to submit on [Enter] and [Space]
actions: {
ActivateIntent: CallbackAction<Intent>(onInvoke: (_) => _handlePressed()),
},
// Add 'Ctrl + X' key to the default [Enter] and [Space]
shortcuts: {
SingleActivator(LogicalKeyboardKey.keyX, control: true): ActivateIntent(),
},
child: Container(
padding: const EdgeInsets.all(8),
child: Text(widget.label),
decoration: BoxDecoration(
color: bgColor,
border: Border.all(color: outlineColor, width: 1),
),
),
),
);
}
void _handlePressed() {
_focusNode.requestFocus();
widget.onPressed();
}
}
class MyCustomCheckbox extends StatefulWidget {
const MyCustomCheckbox({Key? key, this.label = '', required this.value, this.onChanged}) : super(key: key);
final String label;
final bool value;
final void Function(bool value)? onChanged;
@override
State<MyCustomCheckbox> createState() => _MyCustomCheckboxState();
}
class _MyCustomCheckboxState extends State<MyCustomCheckbox> {
bool _isFocused = false;
bool _isHovered = false;
FocusNode _focusNode = FocusNode();
@override
Widget build(BuildContext context) {
return FocusableActionDetector(
focusNode: _focusNode,
// Show hand cursor for entire widget
mouseCursor: SystemMouseCursors.click,
// Toggle the visual state of control when we have focus or hover
onShowFocusHighlight: (v) => setState(() => _isFocused = v),
onShowHoverHighlight: (v) => setState(() => _isHovered = v),
// Define the default activate intent which will fire on [Space] or [Enter] keys
actions: {
ActivateIntent: CallbackAction<Intent>(onInvoke: (_) => _handleChangePressed()),
},
// Add tap handler
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: _handleChangePressed,
child: _buildContent(),
));
}
Widget _buildContent() {
Color? contentColor = _isHovered ? Colors.blue : null;
return Stack(
children: [
// Focus outline
if (_isFocused) Positioned.fill(child: _FocusBorder()),
// Content
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Checkbox
_Check(color: contentColor ?? Colors.black, value: widget.value),
const SizedBox(width: 10),
// Label
Text(widget.label, style: TextStyle(color: _isHovered ? Colors.blue : null)),
],
),
),
],
);
}
void _handleChangePressed() {
_focusNode.requestFocus();
widget.onChanged?.call(!widget.value);
}
}
class _FocusBorder extends StatelessWidget {
@override
Widget build(BuildContext context) =>
Container(decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1)));
}
class _Check extends StatelessWidget {
const _Check({Key? key, required this.color, required this.value}) : super(key: key);
final Color color;
final bool value;
@override
Widget build(BuildContext context) {
return Container(
width: 20,
height: 20,
color: color,
padding: const EdgeInsets.all(4),
child: value ? Container(color: Colors.white) : null,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment