0

How to use late final keyword in Flutter Dart freezed model ?

This code generates successfully and has no static analysis error but it does not compile strangely.

import 'dart:convert';

import 'package:freezed_annotation/freezed_annotation.dart';

part 'fb_story.freezed.dart';
part 'fb_story.g.dart';

@freezed
class FbStory with _$FbStory {
  FbStory._();
  const factory FbStory({
    required String id,
    required String data_str,
    @Default(false) bool imageNotAvailable,
    @Default(false) bool videoNotAvailable,
    String? imageUrl,
    String? videoUrl,
  }) = _FbStory;

  late final Map<String, dynamic> data = jsonDecode(data_str);

  factory FbStory.fromJson(Map<String, dynamic> json) =>
      _$FbStoryFromJson(json);
}

Error:

 Error: A constant constructor can't call a non-constant super constructor.

Before, Freezed used to pioneer the late keyword with @late annotation so I guess there should be a way to make this work. class is still freezed, just lazy

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
TSR
  • 17,242
  • 27
  • 93
  • 197

2 Answers2

1

The error is quite explicit, you cannot have a const constructor with a late variable. This is not an error with freezed, a normal Dart class will also have this error:

class FbStory {
  const FbStory(this.data_str);
  
  final String data_str;
  
  // Can't define the 'const' constructor because the field 'data'
  // is initialized with a non-constant value.
  late final Map<String, dynamic> data = jsonDecode(data_str);
}

To fix your issue I would recommend to remove the const from your const factory FbStory(...).

Or you could create a converter class provided by json_serializable:

class StringToMapConverter implements JsonConverter<Map<String, dynamic>, String> {
  const StringToMapConverter();

  @override
  Map<String, dynamic> fromJson(String json) {
    return jsonDecode(json) as Map<String, dynamic>;
  }

  @override
  String toJson(Map<String, dynamic> object) {
    return jsonEncode(object);
  }
}

Your class FbStory would then look like this:

@freezed
class FbStory with _$FbStory {
  FbStory._();
  const factory FbStory({
    required String id,
    @JsonKey(name: 'data_str')
    @StringToMapConverter()
    required Map<String, dynamic> data,
    @Default(false) bool imageNotAvailable,
    @Default(false) bool videoNotAvailable,
    String? imageUrl,
    String? videoUrl,
  }) = _FbStory;
Guillaume Roux
  • 6,352
  • 1
  • 13
  • 38
  • This would work but it defeats the purpose of the `late` keyword. It is not lazily initialized this way, it becomes eagerly initialized – TSR Jul 30 '23 at 00:54
  • @TSR then you can stick with the beginning of my answer and remove the `const`. – Guillaume Roux Jul 30 '23 at 11:22
-1

Just remove const at factory FbStory

Full code:

import 'dart:convert';

import 'package:freezed_annotation/freezed_annotation.dart';

part 'fb_story.freezed.dart';
part 'fb_story.g.dart';



@freezed
class FbStory with _$FbStory {
  FbStory._();
  factory FbStory({
    required String id,
    required String data_str,
    @Default(false) bool imageNotAvailable,
    @Default(false) bool videoNotAvailable,
    String? imageUrl,
    String? videoUrl,
  }) = _FbStory;

  late final Map<String, dynamic> data = jsonDecode(data_str);

  factory FbStory.fromJson(Map<String, dynamic> json) =>
      _$FbStoryFromJson(json);
}
TSR
  • 17,242
  • 27
  • 93
  • 197