sfMaps: _shapeSource@60361165' has not been initialized

Hello,


I am new to sfMaps. I trying to connect sfMap to use dynamic data from api endpoint. I receive the following error:


Exception has occurred.

LateError (LateInitializationError: Field '_shapeSource@60361165' has not been initialized.)


Appreciate your advice to resolve this issue, Please find my code below:

class _MapTestState extends State<MapTest> {

  late MapShapeSource _shapeSource;

  String get firstURL =>

      'http://…;

  late List<MapModel> _data;


  Future<void> fetchData() async {

    String basicAuth = 'Basic ${base64Encode(

      utf8.encode('$username:$password'),

    )}';


    http.Response firstResponse = await http.get(

      Uri.parse(firstURL),

      headers: <String, String>{'authorization': basicAuth},

    );

    if (firstResponse.statusCode == 200) {

      final json1 = jsonDecode(firstResponse.body);


      setState(() {

        final List<dynamic> data1 = json1['rows'];

        _data = data1.map(getMapData).toList();

        //_shapeSource = [];

        _shapeSource = MapShapeSource.asset(

          'assets/maps/json/vn.json',

          shapeDataField: "name",

          dataCount: _data.length,

          primaryValueMapper: (int index) {

            return _data[index].name!;

          },

          shapeColorValueMapper: (int index) {

            return _data[index].count;

          },

          shapeColorMappers: [

            const MapColorMapper(from: 0, to: 100, color: Colors.amberAccent),

            const MapColorMapper(

                from: 101, to: 200, color: Colors.orangeAccent),

            const MapColorMapper(from: 201, to: 400, color: Colors.redAccent),

          ],

        );

      });

    } else {

      throw Exception('Failed to load data');

    }

  }


  @override

  void initState() {

    _data = [];

    //_shapeSource = [];

    super.initState();

    fetchData();

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        centerTitle: true,

        title: const Text('Map Test'),

        actions: <Widget>[

          IconButton(

            icon: const Icon(Icons.settings),

            onPressed: () {

              // do something

            },

          ),

        ],

      ),

      backgroundColor: Colors.white,

      body: Column(children: <Widget>[

        const Padding(

          padding: EdgeInsets.all(5),

          child: Text(

            'Total Confirmed Cases',

            softWrap: true,

            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),

          ),

        ),

        Expanded(

          child: Padding(

            padding: const EdgeInsets.all(2),

            child: SfMaps(layers: [

              MapShapeLayer(

                zoomPanBehavior: zoomPanBehavior,

                color: const Color.fromARGB(255, 206, 250, 208),

                source: _shapeSource!,

                showDataLabels: true,

              ),

            ]),

          ),

        )

      ]),

    );

  }

}


class MapModel {

  final String? name;

  final double? count;

  String? color;

  MapModel({this.name, this.count, this.color});

}


MapModel getMapData(dynamic row) {

  final MapModel mapModel = MapModel(

    name: row[1],

    count: double.tryParse(row[2]) ?? 0,

  );


  final String color;

  if (row[2] == 0) {

    color = "#daf2b9";

  } else if (double.tryParse(row[2])! >= 1 && double.tryParse(row[2])! <= 100) {

    color = "#54de03";

  } else if (double.tryParse(row[2])! >= 101 &&

      double.tryParse(row[2])! <= 200) {

    color = "#97d806";

  } else {

    color = "#d2d009";

  }


  mapModel.color = color;

  return mapModel;

}


7 Replies 1 reply marked as answer

HK Hariharasudhan Kanagaraj Syncfusion Team August 29, 2023 12:23 PM UTC

Hi Sao,


We would like to inform you that the mentioned late exception occurs when we access the late variable before initializing it. Unfortunately, we are unable to replicate the mentioned exception on our end because the provided code snippet is not in a runnable condition as it requires a username and password.


However, based on the provided code snippet, we understand that you are fetching data from the API. If the data is fetched successfully, you initialize the shapeSource variable using the MapShapeSource.asset method. If the data is not fetched successfully, then the shapeSource variable is assigned directly to the source property in the MapShapeLayer before it is initialized, which causes the late exception.


To avoid this late exception, please consider the following points:

  1. Make sure to access the shapeSource variable only after it has been initialized.
  2. You can also make the shapeSource variable nullable instead of using late. This way, when the data is not fetched successfully, you can assign null to the shapeSource to prevent the late exception.
  3. Alternatively, you can initialize the shapeSource variable in the initState method.


If you have any further queries, please feel free to reach out to us.


Regards,

Hari Hara Sudhan K.



SA Sao September 5, 2023 07:44 AM UTC

Dear  Hari Hara Sudhan K.


1 - Following your number 2 recommendation to make shapesource nullable, and Exception error has occured


Image_1672_1693898974722


2 - Following your #3 recommendation, won't work either because I fetch data from api which is available only after the initState()

Image_3569_1693899455144

and cause "

Exception has occurred.

_AssertionError ('package:syncfusion_flutter_maps/src/layer/shape_layer.dart': Failed assertion: line 168 pos 16: '(primaryValueMapper != null && dataCount > 0) ||

            primaryValueMapper == null': is not true.)"



Image_3332_1693899606747

This would made me conclude that sfMap work best with static data provided in the iniState but not with dynamic data obtain from API though late binding.

Do you have recommendation to make sfMap work with data from API (late binding)?

Best regrads


Sao



HK Hariharasudhan Kanagaraj Syncfusion Team September 6, 2023 01:31 PM UTC

Hi Sao,


As already mentioned, the late exception occurs when you try to access the late variable before it has been initialized. Based on the provided code snippet, we understand that you are attempting to fetch data from the API and set the _shapeSource within the fetchData method, which is called asynchronously. Therefore, when the initState method is executed, it does not waits for the asynchronous fetchData method to complete and tries to access the _shapeSource assigned to the source property in the MapShapeLayer before it has been initialized. This leads to the mentioned late exception.


Additionally, the mentioned assertion error (primaryValueMapper != null && dataCount > 0) ||primaryValueMapper == null': is not true.) will occurs when dataCount is zero while the primaryValueMapper property has been given. As the primaryValueMapper property will return the primary value for the every data in the data source collection, the value of the dataCount must be greater than zero when the primaryValueMapper property is given.


However, you can use the FutureBuilder widget to wait for the fetchData method to complete by calling the fetchData method in the future property and by rendering the SfMaps in builder property only when the connection state is completed. Otherwise, you can render an empty container or loading widget.


Kindly refer the code snippet below.

class MapTest extends StatefulWidget {

  const MapTest({Key? key}) : super(key: key);

 

  @override

  State<MapTest> createState() => _MapTestState();

}

 

class _MapTestState extends State<MapTest> {

  late MapShapeSource _shapeSource;

 

  String get firstURL =>

      'http://...'; // Replace with your actual API endpoint URL

 

  late List<MapModel> _data;

 

  Future<void> fetchData() async {

    String basicAuth = 'Basic ${base64Encode(

      utf8.encode('$username:$password'),

    )}';

 

    http.Response firstResponse = await http.get(

      Uri.parse(firstURL),

      headers: <String, String>{'authorization': basicAuth},

    );

 

    if (firstResponse.statusCode == 200) {

      final json1 = jsonDecode(firstResponse.body);

 

      setState(() {

        final List<dynamic> data1 = json1['rows'];

        _data = data1.map(getMapData).toList();

 

        _shapeSource = MapShapeSource.asset(

          'assets/maps/json/vn.json', // Replace with your shapefile data

          shapeDataField: "name",

          dataCount: _data.length,

          primaryValueMapper: (int index) {

            return _data[index].name!;

          },

          shapeColorValueMapper: (int index) {

            return _data[index].count;

          },

          shapeColorMappers: [

            const MapColorMapper(from: 0, to: 100, color: Colors.amberAccent),

            const MapColorMapper(

                from: 101, to: 200, color: Colors.orangeAccent),

            const MapColorMapper(from: 201, to: 400, color: Colors.redAccent),

          ],

        );

      });

    } else {

      throw Exception('Failed to load data');

    }

  }

 

  @override

  void initState() {

    _data = [];

    super.initState();

  }

 

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        centerTitle: true,

        title: const Text('Map Test'),

        actions: <Widget>[

          IconButton(

            icon: const Icon(Icons.settings),

            onPressed: () {

              // do something

            },

          ),

        ],

      ),

      backgroundColor: Colors.white,

      body: Column(

        children: <Widget>[

          const Padding(

            padding: EdgeInsets.all(5),

            child: Text(

              'Total Confirmed Cases',

              softWrap: true,

              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),

            ),

          ),

          Expanded(

            child: FutureBuilder<void>(

              future: fetchData(),

              builder: (context, snapshot) {

                if (snapshot.connectionState == ConnectionState.done) {

                  return Padding(

                    padding: const EdgeInsets.all(2),

                    child: SfMaps(

                      layers: [

                        MapShapeLayer(

                          color: const Color.fromARGB(255, 206, 250, 208),

                          source: _shapeSource,

                          showDataLabels: true,

                        ),

                      ],

                    ),

                  );

                } else {

                  return Container();

                }

              },

            ),

          ),

        ],

      ),

    );

  }

}

 

class MapModel {

  final String? name;

  final double? count;

  String? color;

 

  MapModel({this.name, this.count, this.color});

}

 

MapModel getMapData(dynamic row) {

  final MapModel mapModel = MapModel(

    name: row[1],

    count: double.tryParse(row[2]) ?? 0,

  );

 

  final String color;

  if (row[2] == 0) {

    color = "#daf2b9";

  } else if (double.tryParse(row[2])! >= 1 && double.tryParse(row[2])! <= 100) {

    color = "#54de03";

  } else if (double.tryParse(row[2])! >= 101 &&

      double.tryParse(row[2])! <= 200) {

    color = "#97d806";

  } else {

    color = "#d2d009";

  }

 

  mapModel.color = color;

  return mapModel;

}


You can modify the shared code snippet according to your needs and if you have further queries, please get back to us.


Regards,

Hari Hara Sudhan. K.



SA Sao September 7, 2023 01:36 AM UTC

Dear Hari Hara Sudhan,

Thank you very much for your solution. However, after adopting solution you provide, 

1 - Map is not shown on page

2 - When page is close, the following error occurred

"Exception has occurred.

FlutterError (setState() called after dispose(): _MapTestSixDynamicState#9b3c9(lifecycle state: defunct, not mounted)


This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.


The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.


This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().)".


Could you please advice?

Sao







HK Hariharasudhan Kanagaraj Syncfusion Team September 8, 2023 01:27 PM UTC

Hi Sao,


Query 1 : Map is not shown on page.


As mentioned in the earlier response, we have used the FutureBuilder widget to render the SfMaps widget only when the data has been fetched from the fetchData method or else we have returned an empty container. Therefore, we suspect that the data has not been fetched properly from the fetchData method and the empty container has been rendered in the else portion.


Query 2 : When page is close, the following error occurred.


Unfortunately, we have missed to mention avoiding the setState method inside the fetchData method as we are using the FutureBuilder to render the SfMaps only when the data has been fetched. Here, we have modified the sample by returning the _shapeSource from the fetchData method to render the SfMaps only when the _shapeSource is initialized or else we will render an empty container based on the snapshot.hasData condition.


Kindly refer the modified code snippet below:

class _MapTestState extends State<MapTest> {

  late MapShapeSource _shapeSource;

 

  String get firstURL =>

      'http://...'; // Replace with your actual API endpoint URL

 

  late List<MapModel> _data;

 

  Future<MapShapeSource> fetchData() async {

    String username = 'YourUsername'; // Replace with your username

    String password = 'YourPassword'; // Replace with your password

    String basicAuth = 'Basic ${base64Encode(

      utf8.encode('$username:$password'),

    )}';

 

    http.Response firstResponse = await http.get(

      Uri.parse(firstURL),

      headers: <String, String>{'authorization': basicAuth},

    );

 

    if (firstResponse.statusCode == 200) {

      final json1 = jsonDecode(firstResponse.body);

      final List<dynamic> data1 = json1['rows'];

 

      _data = data1.map(getMapData).toList();

 

      _shapeSource = MapShapeSource.asset(

        'assets/maps/json/vn.json', // Replace with your shapefile data

        shapeDataField: "name",

        dataCount: _data.length,

 

        primaryValueMapper: (int index) {

          return _data[index].name!;

        },

 

        shapeColorValueMapper: (int index) {

          return _data[index].count;

        },

 

        shapeColorMappers: [

          const MapColorMapper(from: 0, to: 100, color: Colors.amberAccent),

          const MapColorMapper(from: 101, to: 200, color: Colors.orangeAccent),

          const MapColorMapper(from: 201, to: 400, color: Colors.redAccent),

        ],

      );

      return _shapeSource;

    } else {

      throw Exception('Failed to load data');

    }

  }

 

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        centerTitle: true,

        title: const Text('Map Test'),

        actions: <Widget>[

          IconButton(

            icon: const Icon(Icons.settings),

            onPressed: () {

              // do something

            },

          ),

        ],

      ),

      backgroundColor: Colors.white,

      body: Column(

        children: <Widget>[

          const Padding(

            padding: EdgeInsets.all(5),

            child: Text(

              'Total Confirmed Cases',

              softWrap: true,

              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),

            ),

          ),

          Expanded(

            child: FutureBuilder<MapShapeSource>(

              future: fetchData(),

              builder: (context, snapshot) {

                if (snapshot.hasData) {

                  return Padding(

                    padding: const EdgeInsets.all(2),

                    child: SfMaps(

                      layers: [

                        MapShapeLayer(

                          color: const Color.fromARGB(255, 206, 250, 208),

                          source: _shapeSource,

                          showDataLabels: true,

                        ),

                      ],

                    ),

                  );

                } else {

                  return Container();

                }

              },

            ),

          ),

        ],

      ),

    );

  }

}

 

class MapModel {

  final String? name;

 

  final double? count;

 

  String? color;

 

  MapModel({this.name, this.count, this.color});

}

 

MapModel getMapData(dynamic row) {

  final MapModel mapModel = MapModel(

    name: row[1],

    count: double.tryParse(row[2]) ?? 0,

  );

 

  final String color;

 

  if (row[2] == 0) {

    color = "#daf2b9";

  } else if (double.tryParse(row[2])! >= 1 && double.tryParse(row[2])! <= 100) {

    color = "#54de03";

  } else if (double.tryParse(row[2])! >= 101 &&

      double.tryParse(row[2])! <= 200) {

    color = "#97d806";

  } else {

    color = "#d2d009";

  }

 

  mapModel.color = color;

 

  return mapModel;

}


We suggest you check with the modified code snippet and if the mentioned exception occurs further, we require some detailed clarifications regrading the below mentioned points.


  1. We recommend you share the code snippet or sample in a runnable state with all the required details like username and password to reproduce the mentioned exception and behavior.
  2. Also share any recordings or snapshots with the detailed explanation of your use case or requirements.


With that additional information, it will be very useful for us to provide the best possible solution from our end.


Regards,
Hari Hara Sudhan. K.


Marked as answer

SA Sao September 13, 2023 01:50 AM UTC

Dear Hari Hara Sudhan. K.

Thank you for providing this working solution.


Sao



HK Hariharasudhan Kanagaraj Syncfusion Team September 13, 2023 04:56 AM UTC

Hi Sao,


Most Welcome. Kindly get back to us if you have further queries. We are always happy to assist you.


Regards,

Hari Hara Sudhan. K.


Loader.
Up arrow icon