skip to Main Content

I’m having issue with Flutter constructor. I’m coming form javascript background and I am confused why Dart is this much hard to code.

first let me tell how it would look like in ts/js

import {availableMeals} from "MealService";

type MealFilter={isGlutenFree:boolean};
type Meal=unknown; //just for brevity

class GridPage{

    _filteredMeal: Meal[]
    constructor(public filter?: MealFilter ){
        
        this._filteredMeal= availableMeals.filter(x=> {
            if (filter==null){
                return true;
            }
            
            if(filter.isGlutenFree && !x.isGlutenFree){
                return false;
            }
            return true;
        });
        // and alot of other calculations
    }
}

I’m trying to convert the same thing to a flutter widget and I’m failing

my first try was to write something like this

class GridPage extends StatelessWidget {
  const GridPage({super.key, this.filter = const []}){
    filteredFoods = availableMeals.where((meal) {
      if (filter == null) {
        return true;
      }
      return false;
    }).toList();;
  }

  final List<FilterModel>? filter;
  final List<Meal> filteredFoods
}

i’m getting error

Const constructors can’t have a body.
Try removing either the ‘const’ keyword or the body.

if I remove the const then I get error

‘filteredFoods’ can’t be used as a setter because it’s final.
Try finding a different setter, or making ‘filteredFoods’ non-final.

if I remove the final from constructor then I get this error:

This class (or a class that this class inherits from) is marked as ‘@immutable’, but one or more of its instance fields aren’t final: GridPage.filteredFoods

so I tried to find a way to keep the final and const

class GridPage extends StatelessWidget {
  const GridPage({super.key, this.filter = const []});

  final List<FilterModel>? filter;
  final List<Meal> filteredFoods= availableMeals.where((meal) {
      if (filter == null) {
        return true;
      }
      return false;
    }).toList();;
}

now I’m getting error that

The instance member ‘filter’ can’t be accessed in an initializer.
Try replacing the reference to the instance member with a different expression

so reluctantly I turned to colon syntax, to be frank I don’t know how that could scale for big calculation that need to be done once only in the constructor


class GridPage extends StatelessWidget {
  const GridPage({super.key, this.filter = const []})
      : filteredFoods= availableMeals.where((meal) {
           if (filter == null) {
              return true;
           }
           return false;
        }).toList();

  final List<FilterModel>? filter;
  final List<Meal> filteredFoods;
}

I tried to wrap the assignment into a function since there are many of them

class GridPage extends StatelessWidget {
  GridPage({super.key, this.filter = const []})
      : init(filter);

getting error

Expected an assignment after the field name.
To initialize a field, use the syntax ‘name = value’

so now my question is how to handle if I have many calculation need to be done once in constructor time? also apparently can’t use anything with body (for, while, if).
I don’t want to switch to statefull just because of this.
I don’t like the idea of moving this to build method too, why I have to calculate the list when filter haven’t changed yet?
is any way I can hold to stateless and do all calculations once?

2

Answers


  1. Remove the const keyword from the constructor and add late modifier to the filteredFoods field. Using late modifier on a final field allows you to assign value to the field later, but only once.

    class GridPage extends StatelessWidget {
      GridPage({super.key, this.filter = const []}) {
        filteredFoods = availableMeals.where((meal) {
          if (filter == null) {
            return true;
          }
          return false;
        }).toList();
      }
    
      final List<FilterModel>? filter;
      late final List<Meal> filteredFoods;
    }
    

    More about the late final variables: https://dart.dev/null-safety/understanding-null-safety#late-final-variables

    Login or Signup to reply.
  2. I would suggest you to do the calculation and other logic outside of constructor.

    That would mean using stateful widget and doing calculations either in initState() or using a FutureBuilder.

    If not, my suggestion is make filtered foods a getter instead of a final property.

    eg:

    List<Meal> get filteredFoods {
      if(filters==null||filters!.isEmpty()) return availableFoods;
      
      return availableFoods.where((e){
        bool shouldReturnFood = false;
        
        // do other comparisons 
        // I'm using the variable to keep track of whether to return the current item or not
        return shouldReturnFood ;
      });
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search