Tuesday, November 29, 2022
HomeIOS DevelopmentDart Extensions Tutorial: Enhance your Flutter Code

Dart Extensions Tutorial: Enhance your Flutter Code

Discover ways to take your Flutter expertise to the following stage and make your code reusable with one among Dart’s most helpful options: Dart extensions.

You would possibly have already got change into conversant in fundamental Flutter and Dart information. You would possibly even have your first app already revealed. But there’s all the time room for enchancment. Dart Extensions can assist make your code smoother and simplify some code utilization.

This tutorial gained’t train you tips on how to make a full Flutter app; the bottom is already accomplished. Your job will probably be to refactor an already working CatFoodCalculator app with extensions. You’ll discover all their usages, together with:

  • Primary extension creation.
  • Extra superior usages, together with extensions on enums, generics or nullable varieties.
  • Recommendation on when to make use of them and when to not.
Word: This Dart extensions tutorial assumes you understand the fundamentals of Dart and Flutter improvement. If you happen to’re new to Flutter improvement, try Getting Began With Flutter information first.

Getting Began

Obtain the starter mission by clicking the Obtain Supplies button on the prime or backside of the tutorial.

Unzip the downloaded file and open the starter mission positioned in /starter inside your favourite IDE. You possibly can run the mission on each cell gadgets and net browsers.

The CatFoodCalculator app is already working. You’ll solely refactor it to incorporate extensions usages.

Take a look at the recordsdata in lib.

The content of the lib folder

First, open lib/information/meal_data.dart. This can be a information class holding your cat’s meal information.

Proceed with lib/widgets/counter.dart. This can be a UI widget used to extend a counter with buttons as an alternative of getting to kind the brand new worth.

Then, open lib/widgets/meal_info.dart. This widget is a type to kind the really helpful quantity of meals for a cat of a given weight. Word that it additionally holds the MealType enum.

Subsequent, take a look at the widgets in lib/widgets/meal_repartition_result.dart. The MealRepartitionResult widget exhibits the ensuing repartition based mostly on MealData.

Lastly, open lib/fundamental.dart. This incorporates the core of your app.

Specifically, search for MyHomePage and its state _MyHomePageState. The non-public technique _mainColumnContent() returns the primary elements of your UI. The strategies _calculateRation() and _updateCatComment() comprise the enterprise guidelines.

Construct and run the mission. It’s best to see a software to calculate how a lot moist and dry meals your cats want.

The initial state of the CatFoodCalculator app

Play with the values utilizing the textual content fields or the + and buttons. See how the meals repartition modifications consequently.

What Is an Extension Methodology?

On this part, you’ll see what an extension technique is and why it’s helpful.


Creating an extension on a category permits you to add strategies to it with out altering that class. Extensions are helpful for including options to courses you may’t or don’t wish to change.

You may also use them to create shortcuts.

Comparability with Options

When you may’t change a category however wish to add a function associated to it, you’ve gotten three choices:

  1. Use a worldwide or a static technique
  2. Use a wrapper across the desired class and create the strategy in that wrapper
  3. Use an extension technique

See every of them within the examples under:

// 1. Static technique
class StaticMethods {
  static String addCat(String baseString){
    return '$baseString 🐱';

// 2. Wrapper
class WrappedString {
  remaining String baseString;


  String addCat() {
    return '$baseString 🐱';

// 3. Extension
extension Extension on String {
  String addCat(){
    return '$this 🐱';

When beginning with the identical enter String, all three strategies add a ' 🐱' on the finish of the enter. The primary distinction is the way you invoke them.

// 1. Static technique
StaticMethods.addCat('bonjour'); // 'bonjour 🐱'

// 2. Wrapper
WrappedString('bonjour').addCat(); // 'bonjour 🐱'

// 3. Extension
'bonjour'.addCat(); // 'bonjour 🐱'

The extension technique provides a extra fluid API. It feels prefer it’s a basic technique from the bottom class.

Creating and Utilizing a Primary Extension

Now that you understand what Dart extensions are, it’s time to be taught extra about their syntax. You’ll quickly begin including them to the pattern mission.


Take a look at the instance under of a category and its extension.

class ClassToExtend {
  const ClassToExtend({
    required this.aNumber, 
    required this.aString,
  remaining int aNumber;
  remaining String aString;

extension ExtensionName on ClassToExtend {
  String helloWorld() {
    return '$runtimeType says hi there to the world';

  String get hi there => 'hi there $aString';

  int operator +(int different) => aNumber + different;

An extension has a reputation and extends a selected class. Within the instance above, the title is ExtensionName and the prolonged class is ClassToExtend.

Within the extension physique, you may write new strategies, getters and even operators! You possibly can consult with public members of the prolonged class. Within the instance above, you entry aString and aNumber. You possibly can’t entry non-public members of the prolonged class within the extension code.

remaining extendedClass = ClassToExtend(aNumber: 12, aString: 'there');

extendedClass.helloWorld(); // ClassToExtend says hi there to the world
extendedClass.hi there; // hi there there
extendedClass + 8; // 20

You create an object of the prolonged class utilizing a traditional constructor. Then, you invoke the strategies and the operators outlined within the extension as in the event that they had been outlined within the authentic class.

Creating StringCaseConverter Extension

To your first extension within the CatFoodCalculator app, you’ll add the firstLetterUppercase() technique to String. Identify that extension StringCaseConverter.

Begin by creating the folder lib/utils. This folder will comprise all of the extensions you’ll create throughout this tutorial. Then, create the file string_case_converter.dart in it.

You’re now able to create the extension StringCaseConverter. It ought to comprise the firstLetterUppercase() technique, which, when invoked on a String object, returns its capitalized model. If you happen to’d like, attempt to do it your self first. :]

Click on the Reveal button to get this extension’s code.

[spoiler title=”Solution”]
Right here’s the answer:

extension StringCaseConverter on String {
  String firstLetterUppercase() {
    remaining firstLetter = substring(0, 1);
    remaining relaxation = substring(1, size);
    return firstLetter.toUpperCase() + relaxation;


With this, you may convert the primary letter of a String to uppercase with out touching the remainder of the String.

Open lib/widgets/meal_info.dart and find the _title() technique. It returns a Textual content widget that shows “WET meals” or “DRY meals” based mostly on the MealType. The road under transforms the title of the MealType enum to uppercase.

remaining foodType = widget.mealType.title.toUpperCase();

You’ll change this line to rework the title of the MealType enum to make solely the primary letter uppercase.

Begin by importing StringCaseConverter:

import '../utils/string_case_converter.dart';

Now, substitute the foodType project with the next:

remaining foodType = widget.mealType.title.firstLetterUppercase();

Solely the primary letter will probably be uppercase now.

Scorching reload and see the up to date title:

Updated Wet and Dry titles

Word the cat’s weight remark that seems when you set it to a price increased than 7.

Superior Usages

Dart extensions can go means past easy String transformations. You possibly can prolong nullable varieties and generics and might even create non-public extensions.

Nullable Sorts

The cat’s weight feedback don’t begin with an uppercase. You’ll right it utilizing a barely modified model of StringCaseConverter.

Take a look at the _catWeightCommentBuilder() technique in lib/fundamental.dart.

If you happen to’d like to make use of firstLetterUppercase() on _catWeightComment, you’d should cope with the truth that the _catWeightComment variable is nullable.

It may appear like this:


Word the ? to deal with nullable values.

However there’s an excellent simpler method: You may make extensions on nullable varieties.

Substitute StringCaseConverter in lib/utils/string_case_converter.dart with this code:

extension StringCaseConverter on String? {
  String firstLetterUppercase() {
    if (this == null || this!.isEmpty) {
      return '';
    } else {
      remaining firstLetter = this!.substring(0, 1);
      remaining relaxation = this!.substring(1, this!.size);
      return firstLetter.toUpperCase() + relaxation;

Since you deal with the nullable values in firstLetterUppercase(), you don’t want the ? in your technique calls anymore.

Return to lib/fundamental.dart and alter _catWeightCommentBuilder() to make use of the up to date extension:

Widget _catWeightCommentBuilder() {
  return Textual content(
    textAlign: TextAlign.middle,
    fashion: Theme.of(context).textTheme.bodyMedium?.copyWith(
          fontStyle: FontStyle.italic,

Don’t neglect to import the extension.

import '../utils/string_case_converter.dart';

_catWeightComment will now begin with an uppercase.

Scorching reload to see that small change.

The comment text is updated by using Dart extensions on a nullable type


Like common courses and strategies, you may create Dart extensions on generic varieties. You’ll make one to insert a component between every authentic record aspect.

Add a separator between each element of a list

Within the image above, the unique record incorporates numbers you wish to separate by a comma. That is what you wish to obtain together with your extension.

To do that on a generic Record, make an extension on Record<T>, the place “T” is the kind of the weather within the record.

First, create a file named separated_list.dart in lib/utils/, then paste the next code in it:

extension SeparatedList<T> on Record<T> {
  Record<T> separated(T separator) {
    remaining newList = <T>[];
    for (var i = 0; i < size; i++) {
      if (i == 0) {
      } else {
    return newList;

The separated() technique provides a separator between every aspect of the unique Record. Word that each the Record and the brand new aspect needs to be of kind T.

This is an instance of tips on how to use it:

remaining myExampleList = <String>['Sam', 'John', 'Maya'];
print(myExampleList.separated(', ').be a part of()); // Prints "Sam, John, Maya"

The ListView widget has a separated constructor like this.

Now you can obtain one thing resembling it with Column and Row.

In lib/fundamental.dart, find the _mainColumnContent() technique. It returns the kids of the primary Column of your widget tree. Word the area variable on the technique’s starting.

const area = SizedBox(top: 20);

It is used so as to add area amongst all the kids of the Column widget, which is the app’s fundamental construction. Delete that variable and all of the strains the place it seems.

Now, it is advisable to use the brand new extension. Find the remark TODO Add separation between gadgets with an extension and substitute all the line with the code under.

].separated(const SizedBox(top: 20));

With this code, you invoke separated() on the widget record earlier than returning it. The extension technique inserts the SizedBox between every authentic gadgets.

Once more, do not forget to import the extension.

import '../utils/separated_list.dart';

It’s also possible to make an extension technique immediately on Record<Widget> somewhat than on a generic Record. Paste the next code on the finish of lib/utils/separated_list.dart:

extension SpacedWidgets on Record<Widget> {
  // 1. 
  // double defaultHorizontalSpace = 8;

  // 2.
  static const double _defaultHorizontalSpace = 8;
  static const double _defaultVerticalSpace = 8;

  // 3.
  Record<Widget> _spaced(
      {required double horizontalSpace, required double verticalSpace}) {
    // 4.
    return separated(SizedBox(width: horizontalSpace, top: verticalSpace));

  Record<Widget> horizontallySpaced({
    double horizontalSpace = _defaultHorizontalSpace,
  }) {
    return _spaced(horizontalSpace: horizontalSpace, verticalSpace: 0);

  Record<Widget> verticallySpaced({
    double verticalSpace = _defaultVerticalSpace,
  }) {
    return _spaced(horizontalSpace: 0, verticalSpace: verticalSpace);

Within the code above, you create an extension on an inventory of widgets. The extension defines a few strategies that add area among the many widgets within the record.

Some vital limitations and options of Dart extensions are highlighted within the code:

  1. Declaring occasion fields is not allowed.
  2. Implementing static fields is allowed.
  3. You possibly can create non-public strategies inside an extension.
  4. It is doable to reuse different extensions in an extension, like SeparatedList is utilized in SpacedWidgets.

Bear in mind to import the lacking references.

import 'package deal:flutter/widgets.dart';

Due to SpacedWidgets, now you can return to lib/fundamental.dart and substitute your earlier separated() name with the brand new extension.

// Substitute
].separated(const SizedBox(top: 20));

// with
].verticallySpaced(verticalSpace: 20);

You are now utilizing SpacedWidgets as an alternative of SeparatedList.

Personal Dart Extensions

Like courses, you may make extensions non-public by beginning their title with an _.

To make SpacedWidgets non-public, transfer it from lib/utils/separated_list.dart to fundamental.dart since you’ll use it solely there, and rename it to _SpacedWidgets:

extension _SpacedWidgets on Record<Widget>{
  // ...

As a result of it begins with an underscore, it is now non-public; you may solely use it within the fundamental.dart file.

It’s also possible to make extensions non-public by omitting their title:

extension on Record<Widget>{
  // ...

Nevertheless, naming an extension make it simpler to grasp what it does. Furthermore, it provides you a neater approach to handle conflicts, as you may see later.

Though it’d sound good to make non-public extensions, it’s best to establish the place you may reuse them in your code and alter them to be public. Extensions are useful as a result of they make code extremely reusable.

Static Capabilities, Constructors and Factories

Dart extensions aren’t but excellent. They cannot:

  • Create new constructors
  • Create factories

You possibly can declare static capabilities like within the following instance:

extension StringPrinter on String {
  // 1.
  // static String print() {
  //   print(this);
  // }

  // 2.
  static String helloWorld() {
    return 'Hiya world';

This is a breakdown of the code snippet above:

  1. You possibly can’t use this in a static technique. That is as a result of it is static: You make the decision on the category, not on an occasion of the category.
  2. You possibly can outline an everyday static technique.
    However its utilization would possibly disappoint you:

// Would not work
// String.helloWorld();

// Would not work
// 'one thing'.helloWorld();

// Works!

You possibly can’t use String to name helloWorld(). It’s important to use StringPrinter immediately, which is not perfect. With the ability to name String.helloWorld() was the preliminary intention, in spite of everything.

For the CatFoodCalculator app, you might need preferred to return a Slider with a theme included in its constructor as an alternative of getting to wrap the Slider with a SliderTheme.

Copy the next code and paste it in a brand new file lib/utils/themed_slider.dart:

import 'package deal:flutter/materials.dart';

extension ThemedSlider on Slider {
  static Widget withTheme({
    Key? key,
    required double worth,
    required Operate(double) onChanged,
    Operate(double)? onChangeStart,
    Operate(double)? onChangeEnd,
    double min = 0.0,
    double max = 1.0,
    int? divisions,
    String? label,
    Coloration? activeColor,
    Coloration? inactiveColor,
    Coloration? thumbColor,
    MouseCursor? mouseCursor,
    String Operate(double)? semanticFormatterCallback,
    FocusNode? focusNode,
    bool autofocus = false,
    required SliderThemeData themeData,
  }) {
    return SliderTheme(
      information: themeData,
      baby: Slider(
        key: key,
        worth: worth,
        onChanged: onChanged,
        onChangeStart: onChangeStart,
        onChangeEnd: onChangeEnd,
        min: min,
        max: max,
        divisions: divisions,
        label: label,
        activeColor: activeColor,
        inactiveColor: inactiveColor,
        thumbColor: thumbColor,
        mouseCursor: mouseCursor,
        semanticFormatterCallback: semanticFormatterCallback,
        focusNode: focusNode,
        autofocus: autofocus,

The extension wraps the Slider with a SliderTheme as an alternative of getting to cope with it immediately.

Now, in lib/fundamental.dart, import the brand new file with:

import '../utils/themed_slider.dart';

Then, find SliderTheme, proper under the // TODO Substitute SliderTheme with ThemedSlider remark. Substitute SliderTheme, the kid of the Expanded widget, with a name to the brand new extension as within the code under:

baby: ThemedSlider.withTheme(
  worth: _mealRepartition,
  min: 0,
  max: _nbMeals.toDouble(),
  divisions: _nbMeals,
  onChanged: (newVal) {
    setState(() {
      _mealRepartition = newVal;
  themeData: const SliderThemeData(
    trackHeight: 16,
    tickMarkShape: RoundSliderTickMarkShape(tickMarkRadius: 6),
    thumbShape: RoundSliderThumbShape(enabledThumbRadius: 16),
    thumbColor: Coloration(0xffffa938),

It’s important to name ThemedSlider.withTheme() as an alternative of Slider.withTheme(). This limitation is actively mentioned in a GitHub situation.

Dart Extensions on Enums

Apart from courses, you can too create extensions on enum.

Open lib/widgets/meal_info.dart and notice the MealType enum declaration on the prime of the file.

The quantity of meals it’s best to feed to your cat depends upon the precise meals, and the package deal often exhibits the really helpful each day consumption. One won’t know the place to seek out the proper info to kind on this type. That is why there is a Assist button, which shows a popup:

The popups giving more information on how much food a cat should eat

The popup content material modifications based mostly on the MealType. In your subsequent extension, you may create a way to indicate this popup.

Add an extension MealTypeDialog in a brand new file, lib/utils/meal_type_dialog.dart:

import 'package deal:flutter/materials.dart';

import '../widgets/meal_info.dart';

extension MealTypeDialog on MealType {
  Future<void> infoPopup(BuildContext context) {
    remaining textual content = this == MealType.moist
        ? 'You will discover this information printed on the pack of moist meals'
        : 'Your bag of dry meals ought to have this information printed on it';
    return showDialog<void>(
        context: context,
        builder: (context) {
          return AlertDialog(
            content material: Textual content(textual content),
            actions: [
                onPressed: () {
                child: const Text('OK'),

This extension shows the identical dialog you get whenever you use the onInfoPressed() technique from _MealInfoState. It exhibits a unique textual content based mostly on the MealType.

In meal_info.dart, import the file with the brand new extension:

import '../utils/meal_type_dialog.dart';

Then, search for the // TODO Substitute onInfoPressed with an extension remark and substitute the onPressed with a name to the MealTypeDialog extension.

onPressed: () => widget.mealType.infoPopup(context),

The infoPopup() technique now takes care of displaying the dialog. You do not want onInfoPressed() anymore, so you may delete it.

And voilà! Due to your extension, you are now displaying a popup immediately by calling a way on an enum.

Dealing with Conflicts

The CatFoodCalculator app is sort of easy: There is no API name nor native storage. If you would like to implement it, changing your objects to JSON is an effective start line. A technique of doing it’s to make use of jsonEncode().

Create an extension JsonConverter in a brand new file, lib/utils/json_converter.dart:

import 'dart:convert';

extension JsonConverter on dynamic {
// ...

You will want dart:convert since you’ll use jsonEncode(). Word that the extension is dynamic: It is out there to every type, together with your goal class MealData.

Now, add a brand new technique to this extension:

String stringify() {
  return jsonEncode(this);

As you may see, jsonEncode() does all the job.

In fundamental.dart, discover the // TODO add a save button right here remark and substitute it with a Save button as within the code under.

Record<Widget> _mainColumnContent() {
  return [
      onPressed: _saveMealData,
      child: const Text('SAVE'),
  ].verticallySpaced(verticalSpace: 20);

You will use this button to simulate saving MealData in _saveMealData(). Create a brand new technique within the _MyHomePageState widget:

void _saveMealData() {
  remaining mealData = MealData.dry(
    nbMeals: _mealRepartition.spherical(),
    eachAmount: _calculateRation(MealType.dry),

  print('Json : ${mealData.stringify()}');

Import JsonConverter extension:

import 'utils/json_converter.dart';

As an alternative of saving MealData someplace, you may solely print it to the console on this instance, due to print(). That is what it’s best to learn within the console:

   "nbMeals": 3,
   "mealType": "dry",
   "eachAmount": 122

Another stringify technique may embody the kind of the article because the preliminary key:

      "nbMeals": 3,
      "mealType": "dry",
      "eachAmount": 122

Return to json_converter.dart and create one other extension:

extension JsonConverterAlt on dynamic {
  String stringify() {
    return '{$runtimeType: ${jsonEncode(this)}}';

This one consists of the runtimeType as the primary key.

Each JsonConverter and JsonConverterAlt have a way named stringify(). In an actual app, this would possibly occur resulting from utilizing an exterior library.

Return to fundamental.dart and notice the error on stringify():

Word: A member named ‘stringify’ is outlined in extension ‘JsonConverter’ and extension ‘JsonConverterAlt’, and none is extra particular.

One approach to resolve it’s to make use of the cover function within the import:

import 'utils/json_converter.dart' cover JsonConverterAlt;

The error disappears, however you may’t use each extensions on fundamental.dart with this technique.

One other approach to resolve this downside is to make use of the names of your extensions: That is why it’s best to title them. Take away the cover JsonConverterAlt code you added to the import assertion and substitute the physique of the _saveMealData() technique with the next:

remaining mealData = MealData.dry(
  nbMeals: _mealRepartition.spherical(),
  eachAmount: _calculateRation(MealType.dry),

print('Json v1 : ${JsonConverter(mealData).stringify()}');
print('Json v2 : ${JsonConverterAlt(mealData).stringify()}');

Wrapping your class with the extension helps to resolve conflicts after they happen merely, even when the API is a bit much less fluid now.

Widespread Extension Usages

Now that you’ve got discovered what Dart extensions are and tips on how to create them, it is time to see some frequent usages in actual apps.

Including Options to Courses

Extensions allow you to add options to current Flutter and Dart courses with out re-implementing them.

Listed here are a couple of examples:

  • Convert a Coloration to a hex String and vice versa.
  • Separating the kids of a ListView utilizing the identical Widget as a separator in all the app.
  • Convert a lot of milliseconds from an int to a extra humanly readable String.

It’s also possible to add options to courses from exterior packages out there at pub.dev.

Folks usually put the code so as to add these options in Utils courses similar to StringUtils. You would possibly have already got seen that in some initiatives, even in different languages.

Extensions present a superb various to them with a extra fluid API. If you happen to select this method, your StringUtils code will change into an extension as an alternative of a category. Listed here are a couple of strategies you can add to a StringUtils extension:

  • String firstLetterUppercase()
  • bool isMail()
  • bool isLink()
  • bool isMultiline(int lineLength)
  • int occurrences(String sample)

When writing a static technique, think about whether or not an extension would work first. An extension would possibly provide the similar output however with a greater API. That is particularly good when that technique is helpful in a number of locations in your code. :]

Dart Extensions as Shortcuts

In Flutter, many widgets require the present BuildContext, such because the Theme and Navigator. To make use of a TextStyle outlined in your Theme inside the construct() technique of your widgets, you may have to put in writing one thing like this:


That is not brief, and also you would possibly use it a number of instances in your app. You possibly can create extensions to make that type of code shorter. Listed here are a couple of examples:

import 'package deal:flutter/materials.dart';

extension ThemeShortcuts on BuildContext {
  // 1.
  TextTheme get textTheme => Theme.of(this).textTheme;

  // 2.
  TextStyle? get headlineSmall => textTheme.headlineSmall;

  // 3.
  Coloration? get primaryColor => Theme.of(this).primaryColor;

This is a breakdown of the code above:

  1. You make the textTheme extra simply accessible:

// With out extension
// With extension
  1. Use your earlier textTheme technique to return a TextStyle. The code is clearly shorter:

// With out extension
// With extension
  1. You possibly can add as many strategies as you’d prefer to make shortcuts, similar to to get the primaryColor:

// With out extension
// With extension

Widespread Packages Utilizing Extensions

You would possibly already use in style packages that allow you to use extensions.

Routing packages usually use them to navigate immediately from BuildContext. In auto_route as an example, you may go to the earlier web page with context.popRoute(). The identical goes with go_router, the place you should utilize context.pop().

Translation packages present strategies on String through extensions to translate them to the proper language. With easy_localization, you may name tr() in your String to translate it: hi there.tr(). You possibly can even name it on Textual content: Textual content('hi there').tr().

State administration packages like Supplier additionally use them. For example, you may watch a price from a Supplier with context.watch()

You possibly can even seek for extensions on pub.dev, and you will find packages that solely comprise extensions so as to add frequent options to native varieties or for use as shortcuts.

Extensions In every single place … Or not?

Dart extensions give superpowers to your courses. However with nice energy comes nice duty.

Writing shorter code is not all the time the easiest way to make a mission develop, particularly whenever you’re a part of a group. When engaged on a Flutter mission, Flutter and Dart APIs are the frequent base each developer ought to know.

  1. If you happen to rely an excessive amount of on extensions, you may lose familiarity with the overall Flutter and Dart APIs.

You might need difficulties when becoming a member of new initiatives the place extensions aren’t used. It’d take you longer to get conversant in the mission.

  1. Different builders usually are not conversant in your extensions.

If different builders be a part of your mission, they could have issue understanding your code and following your practices. They’re going to should be taught your extensions along with every part else they’re going to have to be taught, just like the enterprise and the structure.

Basically, use extensions however do not rely an excessive amount of on them.

The place to Go From Right here?

Obtain the finished mission recordsdata by clicking the Obtain Supplies button on the prime or backside of the tutorial. Now, it’s best to higher perceive Dart extensions and tips on how to use them in your Flutter apps.

A package deal that makes heavy use of extensions is RxDart.. Be taught extra about it in RxDart Tutorial for Flutter: Getting Began.

We hope you loved this tutorial. When you have any questions or feedback, please be a part of the dialogue under!



Please enter your comment!
Please enter your name here

Most Popular

Recent Comments