Learning how to use OverlayPortal and CompositedTransformTarget to build a custom dropdown in Flutter without external packages!
This is part of an article I wrote in DEV.to, go check it out! :)
| import 'package:flutter/material.dart'; | |
| void main() => runApp(const App()); | |
| final class App extends StatelessWidget { | |
| const App({super.key}); | |
| @override | |
| Widget build(BuildContext context) => MaterialApp( | |
| theme: ThemeData.light(), | |
| darkTheme: ThemeData.dark(), | |
| home: const HomeScreen(), | |
| ); | |
| } | |
| final class HomeScreen extends StatelessWidget { | |
| const HomeScreen({super.key}); | |
| static final _controller = OverlayPortalController(); | |
| static final _link = LayerLink(); | |
| @override | |
| Widget build(BuildContext context) => Scaffold( | |
| appBar: AppBar(title: const Text('Custom dropdown'),), | |
| body: Center( | |
| child: SizedBox( | |
| height: 64, | |
| width: 200, | |
| child: LayoutBuilder( | |
| builder: (context, limits) => OverlayPortal( | |
| controller: _controller, | |
| overlayChildBuilder: (context) => UnconstrainedBox( | |
| child: CompositedTransformFollower( | |
| link: _link, | |
| offset: const Offset(0, 12), | |
| targetAnchor: Alignment.bottomLeft, | |
| child: Container( | |
| width: limits.maxWidth, | |
| height: limits.maxHeight, | |
| decoration: BoxDecoration( | |
| color: Theme.of(context).colorScheme.surfaceContainer, | |
| borderRadius: BorderRadius.circular(16), | |
| ), | |
| alignment: Alignment.center, | |
| child: const Text('Dropdown'), | |
| ), | |
| ), | |
| ), | |
| child: CompositedTransformTarget( | |
| link: _link, | |
| child: ElevatedButton( | |
| onPressed: _controller.toggle, | |
| child: const Text('Toggle dropdown'), | |
| ), | |
| ), | |
| ), | |
| ), | |
| ), | |
| ), | |
| ); | |
| } |