Custom Widget Development in Flutter

This custom widget development is posted by seven.srikanth at 5/2/2025 11:40:55 PM



<h1 id="custom-widget-development-in-flutter">Custom Widget Development in Flutter</h1> <p>Creating custom widgets is a fundamental part of Flutter development. In this article, we'll explore how to create effective custom widgets that are reusable, maintainable, and performant.</p> <h2 id="basic-custom-widget-structure">1. Basic Custom Widget Structure</h2> <h3 id="stateless-custom-widget">Stateless Custom Widget</h3> <pre>class CustomCard extends StatelessWidget { final String title; final String description; final Color backgroundColor;

const CustomCard({ required this.title, required this.description, this.backgroundColor = Colors.white, });

@override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 4, offset: Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.titleLarge, ), SizedBox(height: 8), Text( description, style: Theme.of(context).textTheme.bodyMedium, ), ], ), ); } } </pre> <h3 id="stateful-custom-widget">Stateful Custom Widget</h3> <pre>class AnimatedCustomButton extends StatefulWidget { final String text; final VoidCallback onPressed; final Color color;

const AnimatedCustomButton({ required this.text, required this.onPressed, this.color = Colors.blue, });

@override State&lt;AnimatedCustomButton&gt; createState() =&gt; _AnimatedCustomButtonState(); }

class _AnimatedCustomButtonState extends State&lt;AnimatedCustomButton&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _scaleAnimation;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(milliseconds: 200), vsync: this, ); _scaleAnimation = Tween&lt;double&gt;(begin: 1.0, end: 0.95).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); }

@override Widget build(BuildContext context) { return GestureDetector( onTapDown: (_) =&gt; controller.forward(), onTapUp: () { _controller.reverse(); widget.onPressed(); }, onTapCancel: () =&gt; _controller.reverse(), child: ScaleTransition( scale: _scaleAnimation, child: Container( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), decoration: BoxDecoration( color: widget.color, borderRadius: BorderRadius.circular(8), ), child: Text( widget.text, style: TextStyle(color: Colors.white), ), ), ), ); }

@override void dispose() { _controller.dispose(); super.dispose(); } } </pre> <h2 id="advanced-custom-widget-techniques">2. Advanced Custom Widget Techniques</h2> <h3 id="custom-paint-widget">Custom Paint Widget</h3> <pre>class CustomProgressIndicator extends StatelessWidget { final double progress; final Color color;

const CustomProgressIndicator({ required this.progress, this.color = Colors.blue, });

@override Widget build(BuildContext context) { return CustomPaint( painter: ProgressPainter(progress: progress, color: color), child: Container(), ); } }

class ProgressPainter extends CustomPainter { final double progress; final Color color;

ProgressPainter({required this.progress, required this.color});

@override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = color ..style = PaintingStyle.stroke ..strokeWidth = 4.0;

final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2 - 2;

canvas.drawCircle(center, radius, paint);

final progressPaint = Paint()
  ..color = color
  ..style = PaintingStyle.stroke
  ..strokeWidth = 4.0
  ..strokeCap = StrokeCap.round;

canvas.drawArc(
  Rect.fromCircle(center: center, radius: radius),
  -pi / 2,
  2 * pi * progress,
  false,
  progressPaint,
);

}

@override bool shouldRepaint(ProgressPainter oldDelegate) =&gt; oldDelegate.progress != progress || oldDelegate.color != color; } </pre> <h2 id="best-practices-for-custom-widgets">3. Best Practices for Custom Widgets</h2> <h3 id="proper-parameter-validation">1. Proper Parameter Validation</h3> <pre>class ValidatedCustomWidget extends StatelessWidget { final String title; final int count;

const ValidatedCustomWidget({ required this.title, required this.count, }) : assert(count &gt;= 0, &#39;Count must be non-negative&#39;);

@override Widget build(BuildContext context) { return Container( child: Text(&#39;$title: $count&#39;), ); } } </pre> <h3 id="using-theme-data">2. Using Theme Data</h3> <pre>class ThemedCustomWidget extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); return Container( decoration: BoxDecoration( color: theme.colorScheme.primary, borderRadius: BorderRadius.circular(theme.shape.borderRadius), ), child: Text( &#39;Themed Widget&#39;, style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.onPrimary, ), ), ); } } </pre> <h3 id="implementing-proper-documentation">3. Implementing Proper Documentation</h3> <pre>/// A custom widget that displays a progress indicator with customizable colors. /// /// This widget can be used to show progress in various parts of the application. /// It supports both determinate and indeterminate progress states. /// /// Example: /// dart /// CustomProgressIndicator( /// progress: 0.75, /// color: Colors.blue, /// ) /// class CustomProgressIndicator extends StatelessWidget { // ... implementation } </pre> <h2 id="testing-custom-widgets">4. Testing Custom Widgets</h2> <h3 id="unit-testing">Unit Testing</h3> <pre>void main() { testWidgets(&#39;CustomCard displays correct content&#39;, (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: CustomCard( title: &#39;Test Title&#39;, description: &#39;Test Description&#39;, ), ), );

expect(find.text(&amp;#39;Test Title&amp;#39;), findsOneWidget);
expect(find.text(&amp;#39;Test Description&amp;#39;), findsOneWidget);

}); } </pre> <h2 id="performance-considerations">5. Performance Considerations</h2> <ol> <li><strong>Use const constructors</strong> where possible</li> <li><strong>Implement shouldRepaint</strong> properly in CustomPainter</li> <li><strong>Use RepaintBoundary</strong> for complex widgets</li> <li><strong>Minimize rebuilds</strong> by extracting sub-widgets</li> <li><strong>Use keys</strong> appropriately for state management</li> </ol> <p>By following these guidelines and examples, you can create custom widgets that are:</p> <ul> <li>Reusable across your application</li> <li>Easy to maintain and modify</li> <li>Performant and efficient</li> <li>Well-documented and tested</li> <li>Consistent with your app's design system</li> </ul>


Tags: flutter,markdown,generated








0 Comments
Login to comment.
Recent Comments