Flutter migrating to null safety, late or nullable?

1,305

To answer my own question, or questions... Disclaimer: I have no Dart errors after making the changes below but my code isn't exactly working so the solution might not be 100% correct.

Case 1: Parsing from JSON to dart

Since quicktype is null-safe yet, I have opted for json_serializable do my JSON to dart conversion.

Case 2: Initialising variables

Instead of declaring <FutureList<List>> as nullable, I have added a type to my FutureBuilder.

child: FutureBuilder<List<VocabList>>(

Case 3: Passing data from MaterialPageRoute

I no longer have to declare the variables as nullable.

Case 4: http database helper class

Did not use the nullable constructor but instead used late.

late String someName;

Case 5: sqflite database helper class

New code here:

  // Initialise the database.
  // Only allow a single open connection to the database.
  static Database? _database; // Added ? for null-safety

  Future<Database> get database async {
    if (_database != null)
      return _database as Future<Database>; // Added 'as type' for null-safety
    _database = await _initDatabase();
    return _database as Future<Database>; // Added 'as type' for null-safety
  }

If anybody spots any errors or knows how I can do this better, please let me know!

Share:
1,305
keymistress
Author by

keymistress

Updated on December 31, 2022

Comments

  • keymistress
    keymistress over 1 year

    I would like to migrate to null safety correctly but I am not sure exactly when to use late and when to use nullable. I have read Flutter's Understanding null safety article and finished the Null Safety codelabs but I don't know if I have understood it correctly. New to Flutter and Dart, trying to catch this null safety migration curve ball here in five different scenarios:

    1. Parsing from JSON to dart
    2. Initialising variables
    3. Passing data from MaterialPageRoute
    4. http database helper class
    5. sqflite database helper class

    Case 1: Parsing from JSON to dart

    I am fetching data with http from a MySQL database and using quicktype.io to parse JSON to dart. The non null-safety code of my data model looks like this:

    // To parse this JSON data, do
    //
    //     final someList = someListFromJson(jsonString);
    
    import 'dart:convert';
    
    List<SomeList> someListFromJson(String str) =>
        List<SomeList>.from(json.decode(str).map((x) => SomeList.fromJson(x)));
    
    String someListToJson(List<SomeList> data) =>
        json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
    
    class SomeList {
      SomeList({
        this.someId,
        this.somebody,
      });
    
      String someId;
      String somebody;
    
      factory SomeList.fromJson(Map<String, dynamic> json) => SomeList(
          someId: json["someId"],
          somebody: json["somebody"],
    
      Map<String, dynamic> toJson() => {
            "someId": someId,
            "somebody": somebody,
          };
    }
    

    Using dart migrate, they suggested that I Changed type 'String' to be nullable.

    However, I know for a fact though that in my json data, someId is never and cannot be null, while somebody could be null. Should I still use the ? nullable type for the sake of initialising? My understanding is that I should not use the ! null assertion operator for somebody since it technically does not have a value yet. Well, then does that mean I should use the late keyword instead?

     String? someId;
     String? somebody;
    

    or

    late String someId;
    late String somebody;
    

    Case 2: Initialising variables

    I call SomeList in a Stateful widget on one of my screens as a Future.

    Old code:

    class _SomeScreenState extends State<SomeScreen > {
    
      Future<List<VocabList>> futureList;
    

    Once again it is proposing that I make it nullable, like so:

    Future<List<VocabList > >? futureList;
    

    Am I right to understand that I use the ? nullable type for initialising Future<List<VocabList>>?

    Case 3: Passing data from MaterialPageRoute

    I am passing data from MaterialPageRoute as such:

    MaterialPageRoute(
    builder: (context) => SomeScreen(
    someId: something[index].someId,
    somebody: something[index].somebody,
    

    On the receiving end, the old code looks like this:

    class SomeScreen extends StatefulWidget {
      final String someId;
      final String somebody;
    
      SomeScreen({
        Key? key,
        @required this.someId,
        @required this.somebody,
    

    Again it is recommending I set my two final variables someId and somebody as nullable but should they be nullable or are they just late?

    Should I do this?

    class SomeScreen extends StatefulWidget {
          final String? someId;
          final String? somebody;
        
          SomeScreen({
            Key? key,
            @required this.someId,
            @required this.somebody,
    

    or

    class SomeScreen extends StatefulWidget {
      late final String someId;
      late final String somebody;
    
      SomeScreen({
        Key? key,
        @required this.someId,
        @required this.somebody,
    

    Case 4: http database helper class

    I am passing the variable someName with a button to a http request.

    import 'dart:io';
    
    import 'package:http/http.dart' as http;
    import 'package:test/models/something_list.dart';
    
    List<SomethingList >  _list = [];
    String someName;
    
    class Somebody {
      static const String  url = 'http://localhost/~idunno/api_search.php';
    
      static Future<List<SomeList > >  getData(String someName) async {
        try {
          http.Response  response =
              await http.get(Uri.parse(url + '?q=' + someName));
    

    someName cannot be null or else the http request will fail. Should I still declare it as nullable and handle the failure using on FormatException like so?

     List<SomethingList >  _list = [];
            String? someName;
       // some code omitted
       on FormatException {
          throw InvalidFormatException('Something is not right here');
    

    Case 5: sqflite database helper class

    Old code:

    static Database _database;
    
      Future<Database>  get database async {
        if (_database != null) return _database;
        // lazily instantiate the db the first time it is accessed
        _database = await _initDatabase();
        return _database;
      }
    
      // some code omitted
    
     Future<bool >  checkBookmark(String someId) async {
        Database  db = await instance.database;
        var bookmarked = await db.query(table,
            where: 'someId = ?',
            whereArgs: [someId]);
        return bookmarked.isEmpty ? false : true;
      }
    

    Two questions here: (1) Like the above-mentioned scenarios, do I make Database and Future<Database> nullable because of initialisation? (2) What does Added a cast to an expression (non-downcast) mean?

    Suggested null safety changes:

    static Database? _database;
    
      Future<Database?>  get database async {
        if (_database != null) return _database;
        // lazily instantiate the db the first time it is accessed
        _database = await _initDatabase();
        return _database;
      }
    
      // some code omitted
    
     Future<bool >  checkBookmark(String? someId) async {
        Database  db = await (instance.database as FutureOr<Database>);
        var bookmarked = await db.query(table,
            where: 'someId = ?',
            whereArgs: [someId]);
        return bookmarked.isEmpty ? false : true;
      }
    

    Any help on this will be much appreciated and I hope the answers will help other new coders with migrating to null safety as well!