Skip to content

Instantly share code, notes, and snippets.

@Plumillon
Created March 5, 2024 17:22
Show Gist options
  • Save Plumillon/8effb10db0af11fb927baa04284ef7f5 to your computer and use it in GitHub Desktop.
Save Plumillon/8effb10db0af11fb927baa04284ef7f5 to your computer and use it in GitHub Desktop.
TabPanel part 1
/// TabPanel part 1
/// This is the first part of the TabPanel series:
/// https://medium.com/@FlavienNorindr/building-a-custom-vertical-tabbed-panel-widget-tabpanel-part-1-7fa7a44f6467
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TabPanel part 1',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const TabPanelPage(),
);
}
}
class TabPanelPage extends StatelessWidget {
const TabPanelPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFED700),
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
const FlutterLogo(size: 400),
ShadowClipPath(
shadow: BoxShadow(
color: Colors.black.withAlpha(200),
blurRadius: 10,
offset: Offset.zero,
spreadRadius: 0,
blurStyle: BlurStyle.outer),
clipper: TabPanelClipper(tabSize: const Size(90, 90), radius: 10),
child: Container(
width: 270,
height: 270,
color: Colors.green.withOpacity(0.8),
),
),
],
)),
);
}
}
class TabPanelClipper extends CustomClipper<Path> {
final Size tabSize;
final double radius;
TabPanelClipper({
super.reclip,
required this.tabSize,
required this.radius,
});
@override
Path getClip(Size size) {
return Path()
..moveTo(tabSize.width + radius, 0)
..quadraticBezierTo(tabSize.width, 0, tabSize.width, radius)
..lineTo(tabSize.width, tabSize.height - radius)
..quadraticBezierTo(
tabSize.width, tabSize.height, tabSize.width - radius, tabSize.height)
..lineTo(radius, tabSize.height)
..quadraticBezierTo(0, tabSize.height, 0, tabSize.height + radius)
..lineTo(0, tabSize.height * 2 - radius)
..quadraticBezierTo(0, tabSize.height * 2, radius, tabSize.height * 2)
..lineTo(tabSize.width - radius, tabSize.height * 2)
..quadraticBezierTo(tabSize.width, tabSize.height * 2, tabSize.width,
tabSize.height * 2 + radius)
..lineTo(tabSize.width, tabSize.height * 3 - radius)
..quadraticBezierTo(tabSize.width, tabSize.height * 3,
tabSize.width + radius, tabSize.height * 3)
..lineTo(size.width - radius, tabSize.height * 3)
..quadraticBezierTo(size.width, tabSize.height * 3, size.width,
tabSize.height * 3 - radius)
..lineTo(size.width, radius)
..quadraticBezierTo(size.width, 0, size.width - radius, 0)
..close();
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
class ShadowClipPath extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Path> clipper;
final Widget child;
final Clip clipBehavior;
const ShadowClipPath({
super.key,
required this.shadow,
required this.clipper,
required this.child,
this.clipBehavior = Clip.antiAlias,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: ShadowClipPainter(
clipper: clipper,
shadow: shadow,
),
child: ClipPath(
clipper: clipper,
clipBehavior: clipBehavior,
child: child,
),
);
}
}
class ShadowClipPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<Path> clipper;
ShadowClipPainter({required this.shadow, required this.clipper});
@override
void paint(Canvas canvas, Size size) {
var paint = shadow.toPaint();
var clipPath = clipper.getClip(size).shift(shadow.offset);
canvas.drawPath(clipPath, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment