In this Article, we are going to create a Generic Type widget In Flutter. We will use the Toggle Button widget to create a Widget in Flutter.
Let’s talk about First of all What is Generic Type in Flutter/Dart?. Then we’ll gonna use the generic type to create a Generic Type Widget in Flutter.
Before we start the blog If you’re looking for a Flutter app development company, we are the perfect choice.
Flutter code uses Dart generics all over the place to ensure object types are what we expect them to be. Simply put, generics are a way to code a class or function so that it works with a range of data types instead of just one while remaining type-safe. The type the code will work with is specified by the caller, and in this way, type safety is maintained. You could write code to explicitly work with Dart’s dynamic type and that would be generic, but not safe
Generics are utilized to apply more grounded type checks at the compile time. They implement type-safety in code. For instance, in collections, the type-safety is authorized by holding a similar kind of information. Generics help composes reusable classes, methods/functions for various information types.
Generics with Collections:
You can declare collection variables without generics like this:
1 2 |
List myList; Map myMap; |
That code is equivalent to the following:
1 2 |
List<dynamic> myList; Map<dynamic, dynamic> myMap; |
This should only be done when you really need a collection containing many different types. If you know the intended type of a list’s elements, you should specify that type within the angle brackets, which will allow the Dart analyzer to help you avoid errors. Similarly, if you intend for a map to contain keys and values of a particular type, include them in the declaration:
1 2 3 |
List<String> myList; Map<String, dynamic> jsonData; Map<int, String> myMap; |
Generics In Flutter
The most common places you’ll make direct use of generics in Flutter are in collections and stateful widgets. This example shows both:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class MyWidget extends StatefulWidget { @override _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> { @override Widget build(BuildContext context) { return Row( children: <Widget>[ const Text("Generic Type Widget "), const Text("in Flutter"), ], ); } } |
Stateful widgets have both a StatefulWidget class and an accompanying State class. The State class uses generics to make sure it deals only with the StatefulWidget it belongs to, using the syntax State<MyWidget>
. A State instance is generic, written to work with any StatefulWidget, and in this case we’re creating a state specifically for our MyWidget class. If you were to leave off the angle brackets and the MyWidget symbol, the analyzer wouldn’t complain, but you would have created a State tied to the default dynamic
type. Not only is this not type safe, but it could create problems for the framework as it tries to match state instances to the correct widgets.
The List literal passed to children
for the Row widget is similarly typed, this time as a list of Widget objects: <Widget>[]
. This designation helps Dart catch problems at design time. If you try to place a non-widget into that collection, you will get warnings. A Row doesn’t know what to do with objects that aren’t widgets, so catching this kind of problem before your code runs is very useful. The generics also serve to create code that is more self-documenting, making it clear what’s expected within the collection.
Now, we’ll work on how to use Generic Type Widget in Flutter.
Create a Widget with the name of SingleOptionPicker.dart:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class SingleOptionPicker<T extends SelectableOption> extends StatelessWidget { const SingleOptionPicker( {Key? key, required this.options, required this.setSingleOption}) : super(key: key); final Map<T, int> options; final Function(T) setSingleOption; @override Widget build(BuildContext context) { return OptionsPicker<T>( options: options, setOption: (Set<T> options) => setSingleOption(options.first), allowsMultiple: false, ); } } |
Let’s understand how things will work in this SingleOptionPicker Widget.
T represents Typedef in Flutter/Dart and SelectableOption is an abstract class that is containing color properties for color use. It means T should have to be only SelectableOption Class.
1 2 3 4 5 |
abstract class SelectableOption { Color get color; const SelectableOption(); } |
OptionsPicker is also uses the T for typedef and options is used for containg data and setSingleOptions Function to chnage the button selection.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
class OptionsPicker<T extends SelectableOption> extends StatefulWidget { const OptionsPicker( {required this.options, required this.setOption, this.allowsMultiple = false, Key? key}) : super(key: key); final Map<T, int> options; final Function(Set<T>) setOption; final bool allowsMultiple; @override _OptionsPickerState<T> createState() => _OptionsPickerState<T>(); } class _OptionsPickerState<T extends SelectableOption> extends State<OptionsPicker<T>> { late Set<int> _selectedOptions; late List<T> optionsList; @override void initState() { super.initState(); _selectedOptions = <int>{0}; optionsList = widget.options.keys.toList(); } @override Widget build(BuildContext context) { List<Widget> textToggles = []; widget.options.forEach((T key, int quantity) { textToggles.add( Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15), color: key.color, child: Text( key.toString().toUpperCase(), style: TextStyle(color: Theme.of(context).colorScheme.secondary), ), ), ); }); List<bool> _activeIndices = List.generate( optionsList.length, (int index) => _selectedOptions.contains(index)); return ToggleButtons( children: textToggles, isSelected: _activeIndices, onPressed: (int index) { setState(() { if (!widget.allowsMultiple) { if (_selectedOptions.contains(index)) { return; } _selectedOptions ..clear() ..add(index); } else { if (_selectedOptions.contains(index)) { if (_selectedOptions.length > 1) { _selectedOptions.remove(index); } } else { _selectedOptions.add(index); } } Set<T> _selectedOptionsNames = {}; for (int index in _selectedOptions) { _selectedOptionsNames.add(optionsList[index]); } widget.setOption(_selectedOptionsNames); }); }, ); } } |
Now look into the Type Class uses for Color and Sizes.
1 2 3 4 5 6 7 8 9 10 11 |
class ProductSize extends SelectableOption { const ProductSize(this.name); final String name; @override String toString() => name; @override Color get color => Colors.transparent; } |
Product Sizes class extends the SelectableOption class, As you can see for sizes we’ve provided the one transparent color and only passes the name key in constructer. Because we only need to use the text for sizes.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class ProductColor extends SelectableOption { final String name; @override final Color color; const ProductColor({required this.name, required this.color}); @override String toString() { return name; } } |
ProductColor Class is also extended to the SelectableOption. But in this case, we required both color and name. So we passed the name and color in constructer with the required keyword.
Now Focus on the HomePageWidget were we’ve used the Our generic type widget.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { Map<String, int> sizes = {"xs": 5, "s": 3, "m": 8, "l": 5, "xl": 3, "xxl": 3}; void setSize(ProductSize size) {} void setColor(ProductColor color) {} @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Generic Type Widget"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const SizedBox(height: 15), Center( child: SingleOptionPicker<ProductSize>( options: sizes.map<ProductSize, int>((String name, int quantity) => MapEntry<ProductSize, int>(ProductSize(name), quantity)), setSingleOption: setSize, ), ), const SizedBox(height: 15), Center( child: SingleOptionPicker<ProductColor>( options: const { ProductColor(name: "Green", color: Colors.green): 5, ProductColor(name: "Black", color: Colors.black): 9, ProductColor(name: "Pink", color: Colors.pink): 10, ProductColor(name: "Orange", color: Colors.orange): 4, }, setSingleOption: setColor, ), ), ], ), ), ); } } |
We’ve created the Map type sizes object to use diffrent sizes. For color selection widget we’ve passed the key and color value in ProductColor class which uses as a Key in Options and int value 5,9,10 are values. Keep it simple in mind as:
1 2 3 4 5 6 7 8 |
Map<ProductColor, int> colorType; colorType = { ProductColor(name: "Green", color: Colors.green): 5, ProductColor(name: "Black", color: Colors.black): 9, ProductColor(name: "Pink", color: Colors.pink): 10, ProductColor(name: "Orange", color: Colors.orange): 4, }, |
SetSizes and SetColor function are also uses the typedef becuase we’ve created the function to be as typedef.
Now, lets move on to the main function.
we had only removed the unnecessary comment and code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Generic Type Widget Demo', debugShowCheckedModeBanner: false, theme: ThemeData( brightness: Brightness.dark, primarySwatch: Colors.blue, ), home: const MyHomePage(), ); } } |
Now run the app and you’ll see the magic.
Now, Lets come to the point.
Why do we need to use the generic type widget while we can create two different widgets.
If you noticed we’ve used the minimal line of code to create widgets for two different things. As flutter developers, we should always try to code minimal and efficient.
That’s all for this Article 🎊 .
Conclusion
Generic Type Widget in Flutter in this blog we’ve learned how we can create a widget as generic type in a flutter.
If you want to learn more about the generic type widget in flutter then visit this video: https://www.youtube.com/watch?v=Ikg7vHJoZjA
You can read more blogs from Mobikul on this website: https://mobikul.com/blog/