I'm working on simple design system using Flutter. I want to highlight a widget upon selection (click), as you can see in the below images button get highlighted upon click. It gets handle and border.
Challenging part: I don't want layout getting changed as additional space taken by handle and border upon click. I want widget, handle and border are overlaid, so that it wouldn't shift the position of other neighbouring widgets.
And after selection
You could also use a Stack
with the overlay bleeding out of the Stack
thanks to a clipBehavior
of Clip.none
.
Just copy paste it in a DartPad to see it in action.
import 'package:flutter/material.dart';
const kcPrimary = Color(0xFF001989);
const kcSecondary = Color(0xFF239689);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Text('Blablablablabla'),
Text('Blablablablabla'),
Text('Blablablablabla'),
PlayButton(),
Text('Blablablablabla'),
Text('Blablablablabla'),
Text('Blablablablabla'),
],
),
),
),
);
}
}
class PlayButton extends StatefulWidget {
const PlayButton({Key? key}) : super(key: key);
@override
State<PlayButton> createState() => _PlayButtonState();
}
class _PlayButtonState extends State<PlayButton> {
bool clicked = false;
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
InkWell(
onTap: () => setState(() => clicked = !clicked),
child: _mainButton,
),
if (clicked) ...[
Positioned.fill(
child: IgnorePointer(
child: _overlayBorder,
),
),
Positioned(
top: -20.0,
left: 0,
child: _overlayTitle,
),
Positioned(top: 0, right: 0, child: _corner),
Positioned(bottom: 0, right: 0, child: _corner),
Positioned(bottom: 0, left: 0, child: _corner),
Positioned(top: 0, left: 0, child: _corner),
],
],
);
}
Widget get _mainButton => Container(
width: 80.0,
height: 40.0,
decoration: BoxDecoration(
border: Border.all(
color: kcPrimary,
width: 3.0,
),
borderRadius: const BorderRadius.all(Radius.circular(12))),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(Icons.play_arrow),
Text('Play'),
],
),
),
);
Widget get _overlayBorder => Container(
decoration: BoxDecoration(
border: Border.all(
color: kcSecondary,
width: 3.0,
),
),
);
Widget get _corner => Container(width: 10, height: 10, color: kcSecondary);
Widget get _overlayTitle => Container(
height: 20.0,
width: 48.0,
color: kcSecondary,
alignment: Alignment.center,
child: const Text(
'Button',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
);
}