<h1 id="complex-flutter-widgets-building-advanced-ui-components">Complex Flutter Widgets: Building Advanced UI Components</h1> <p>Complex widgets combine multiple basic widgets and implement sophisticated behaviors. Let's explore how to build and implement these advanced UI components.</p> <h2 id="custom-form-widgets">1. Custom Form Widgets</h2> <h3 id="custom-form-field">Custom Form Field</h3> <pre>class CustomFormField extends StatefulWidget { final String label; final String? Function(String?)? validator; final void Function(String?)? onSaved;
const CustomFormField({ Key? key, required this.label, this.validator, this.onSaved, }) : super(key: key);
@override _CustomFormFieldState createState() => _CustomFormFieldState(); }
class _CustomFormFieldState extends State<CustomFormField> { final _controller = TextEditingController(); bool _isPasswordVisible = false;
@override Widget build(BuildContext context) { return TextFormField( controller: _controller, obscureText: !_isPasswordVisible, decoration: InputDecoration( labelText: widget.label, suffixIcon: IconButton( icon: Icon( _isPasswordVisible ? Icons.visibility_off : Icons.visibility, ), onPressed: () { setState(() ); }, ), ), validator: widget.validator, onSaved: widget.onSaved, ); }
@override void dispose() { _controller.dispose(); super.dispose(); } } </pre> <h3 id="custom-date-picker">Custom Date Picker</h3> <pre>class CustomDatePicker extends StatefulWidget { final DateTime? initialDate; final void Function(DateTime)? onDateSelected;
const CustomDatePicker({ Key? key, this.initialDate, this.onDateSelected, }) : super(key: key);
@override _CustomDatePickerState createState() => _CustomDatePickerState(); }
class _CustomDatePickerState extends State<CustomDatePicker> { late DateTime _selectedDate;
@override void initState() { super.initState(); _selectedDate = widget.initialDate ?? DateTime.now(); }
Future<void> _selectDate(BuildContext context) async { final DateTime? picked = await showDatePicker( context: context, initialDate: _selectedDate, firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (picked != null && picked != _selectedDate) { setState(() ); widget.onDateSelected?.call(picked); } }
@override Widget build(BuildContext context) { return InkWell( onTap: () => _selectDate(context), child: InputDecorator( decoration: InputDecoration( labelText: 'Select Date', suffixIcon: Icon(Icons.calendar_today), ), child: Text( '\({_selectedDate.day}/\)/$', ), ), ); } } </pre> <h2 id="custom-list-widgets">2. Custom List Widgets</h2> <h3 id="custom-expandable-list">Custom Expandable List</h3> <pre>class ExpandableList extends StatefulWidget { final List<ExpandableItem> items;
const ExpandableList({ Key? key, required this.items, }) : super(key: key);
@override _ExpandableListState createState() => _ExpandableListState(); }
class _ExpandableListState extends State<ExpandableList> { final Map<int, bool> _expandedItems = ;
@override Widget build(BuildContext context) { return ListView.builder( itemCount: widget.items.length, itemBuilder: (context, index) { final isExpanded = _expandedItems[index] ?? false; return Column( children: [ ListTile( title: Text(widget.items[index].title), trailing: Icon( isExpanded ? Icons.expand_less : Icons.expand_more, ), onTap: () { setState(() { _expandedItems[index] = !isExpanded; }); }, ), if (isExpanded) Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: widget.items[index].content, ), ], ); }, ); } } </pre> <h3 id="custom-draggable-list">Custom Draggable List</h3> <pre>class DraggableList extends StatefulWidget { final List<String> items;
const DraggableList({ Key? key, required this.items, }) : super(key: key);
@override _DraggableListState createState() => _DraggableListState(); }
class _DraggableListState extends State<DraggableList> { late List<String> _items;
@override void initState() { super.initState(); _items = List.from(widget.items); }
void _onReorder(int oldIndex, int newIndex) { setState(() { if (oldIndex < newIndex) { newIndex -= 1; } final item = _items.removeAt(oldIndex); _items.insert(newIndex, item); }); }
@override Widget build(BuildContext context) { return ReorderableListView( onReorder: _onReorder, children: _items.map((item) { return ListTile( key: ValueKey(item), title: Text(item), ); }).toList(), ); } } </pre> <h2 id="custom-navigation-widgets">3. Custom Navigation Widgets</h2> <h3 id="custom-bottom-navigation">Custom Bottom Navigation</h3> <pre>class CustomBottomNavigation extends StatefulWidget { final List<BottomNavItem> items; final int initialIndex; final void Function(int)? onIndexChanged;
const CustomBottomNavigation({ Key? key, required this.items, this.initialIndex = 0, this.onIndexChanged, }) : super(key: key);
@override _CustomBottomNavigationState createState() => _CustomBottomNavigationState(); }
class _CustomBottomNavigationState extends State<CustomBottomNavigation> { late int _currentIndex;
@override void initState() { super.initState(); _currentIndex = widget.initialIndex; }
@override Widget build(BuildContext context) { return BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) { setState(() ); widget.onIndexChanged?.call(index); }, items: widget.items.map((item) { return BottomNavigationBarItem( icon: Icon(item.icon), label: item.label, ); }).toList(), ); } } </pre> <h3 id="custom-tab-navigation">Custom Tab Navigation</h3> <pre>class CustomTabNavigation extends StatefulWidget { final List<TabItem> tabs; final int initialIndex; final void Function(int)? onIndexChanged;
const CustomTabNavigation({ Key? key, required this.tabs, this.initialIndex = 0, this.onIndexChanged, }) : super(key: key);
@override _CustomTabNavigationState createState() => _CustomTabNavigationState(); }
class _CustomTabNavigationState extends State<CustomTabNavigation> with SingleTickerProviderStateMixin { late TabController _tabController;
@override void initState() { super.initState(); _tabController = TabController( length: widget.tabs.length, vsync: this, initialIndex: widget.initialIndex, ); _tabController.addListener(() { widget.onIndexChanged?.call(_tabController.index); }); }
@override Widget build(BuildContext context) { return Column( children: [ TabBar( controller: _tabController, tabs: widget.tabs.map((tab) { return Tab( icon: Icon(tab.icon), text: tab.label, ); }).toList(), ), Expanded( child: TabBarView( controller: _tabController, children: widget.tabs.map((tab) => tab.content).toList(), ), ), ], ); }
@override void dispose() { _tabController.dispose(); super.dispose(); } } </pre> <h2 id="best-practices">4. Best Practices</h2> <ol> <li><p><strong>Implement proper state management</strong></p> <ul> <li>Use appropriate state management solutions</li> <li>Handle state changes efficiently</li> <li>Clean up resources properly</li> </ul> </li> <li><p><strong>Optimize performance</strong></p> <ul> <li>Use const constructors</li> <li>Implement proper keys</li> <li>Minimize rebuilds</li> </ul> </li> <li><p><strong>Handle edge cases</strong></p> <ul> <li>Test thoroughly</li> <li>Handle errors gracefully</li> <li>Provide fallback UI</li> </ul> </li> </ol> <p>By mastering these complex widgets and following best practices, you can create Flutter applications that are:</p> <ul> <li>More sophisticated</li> <li>More maintainable</li> <li>More performant</li> <li>More user-friendly</li> </ul>