Mastering Icon: How does it work from the inside?
In this article we will check out the Icon widget and will talk about a lifehack thanks to which values for an icon can be set dynamically: for example, to fetch it from the server.
This article is a translation of our original article in Ukrainian
The Icon widget
The Flutter's Material Framework contains a widget called Icon. This widget is used to, obviously, display icons in a Flutter app. Its difference from the Image widget lies in the fact the Icon widget is controlled by app's theme, and it integrates with its parental widgets (for example, it changes its color in IconButton widget).
Let's see arguments this widget accepts:
The example above displays an icon from the Material Icons collection. Let's check the arguments:
- First positional argument: IconData instance. We'll talk about it below
- Non-positional
color
argument: Color instance. Used to recolor the icon - Non-positional
size
argument: real number, icon's size in pixels
ImageIcon widget
This widget accepts a raster image for straightforward use of it as an icon:
Syntax is similar to Icon, except the first argument is an instance of ImageProvider. The ImageProvider object is responsible for retrieving images for other widgets. For example, for the Image widget.
It's likely you'll never ever use ImageProvider directly, but will rely on one of the ready implementations instead:
- AssetImage β retrieve an image from Flutter assets (see docs about adding resources to assets)
- NetworkImage β download an image using HTTP
- FileImage β retrieve an image from a File instance
- MemoryImage β build an image from a byte array in memory
- ExactAssetImage β similar to AssetImage, see docs to find out the differences
In this example we use an image from our assets.
Need to mention, color icons are going to be reduced to monochrome ones based on alpha (transparency) channel:
Icons and CupertinoIcons collections
The Icons and CupertinoIcons classes are collections of icons for Material Design and iOS accordingly. Need to mention, the collections are not cross-compatible: if you are developing an app which uses Material icons on Android and Cupertino icons on iOS β you will have to set the relation be yourself or, for example, you can use the flutter_platform_widgets library, which provides a limited set of icons with relations set.
The list of all the icons is provided on Material Icons page. Each icon is available in four styles: Regular, Rounded, Sharp, Outline. To use a needed style, you just need to add a corresponding ending:
All available Cupertino icons are listed on the library page.
Icons and Themes
All the Material Framework components are depending on the set theme. The theme is being defined in the MaterialApp widget using the ThemeData object in theme
property.
You can use the style globally for all the icons in your app using the iconTheme
property in ThemeData:
Except from this, you can rewrite the theme directly in the widget tree by using the IconTheme widget (also, properties from the theme in MaterialApp stop working under this one):
How vector icons work
For vector icons, Flutter uses special icon fonts, the fonts which contain icons in place of characters. So, when you use an Icon widget, Flutter creates something like a Text widget with a specially set font. We'll make sure about this later.
IconData type
IconData is the container for the icon data to be drawn. Instances of this type are stored in the Icons collection: for example, Icons.people
is an IconData object. Let's see the constructor of this object:
First positional argument: integer of a character code which corresponds to an icon
Non-positional fontFamily
argument: font name which is used to display the icon
So, we can reintroduce this behavior using the Text widget (\uef85
is the character code of the icon):
Furthermore, you can use label name instead of character code:
Need to mention, IconData is a constant constructor, that lets the Dart compiler to exclude unused icons.
Dynamic icons and Material Framework integration
β Use the technique described below only if you really need it (to store all the icons on client side and provide a needed icon name dynamically), since this hurts the app size. Instead of using this, you could try ImageIcon together with, for example, NetworkImage.
So, we make a conclusion we can use the Text widget to display the icon by its code or label. Thought, this approach has a huge downside: it doesn't integrate with the Material Framework, since Flutter treats this widget as a usual text, not an icon. So let's create own Icon class which is not constant and accepts an icon name to draw (see end of the article for the implementation).
Use:
Warning! Due to the fact the constructor is not constant, tree shaking will fail, so the app won't compile. This behavior can be disabled by the --no-tree-shake-icons
build flag.