1

I want to save AppInfo information from API into SharedPreferences. I have source code and result JSON like this :

APP_INFO_API.dart:

  Future<List> getAppInfo() async {
    try {
      final response = await http.get(
          "${appConfig.baseApiUrl}/${appConfig.appInfoController}/getLogoClient");
      final Map<String, dynamic> responseJson = json.decode(response.body);
      if (responseJson["status"] == "ok") {
        List appInfoList = responseJson["data"];
print('List Dynamic From API : $appInfoList ');
        return appInfoList;
      } else {
        throw CustomError(responseJson["message"]);
      }
    } catch (e) {
      return Future.error(e.toString());
    }
  }

Result JSON

 List Dynamic From API : [{kodeInfo: 1, namaInfo: Integrasi Operasional Outlet, keteranganInfo: Burger Klenger ERPOS Version 1.2, fileInfo: img-1.png, logoInfo: logo-1.png, loginBackgroundInfo: bfront-1.png, loginLeftInfo: bleft-1.png, loginSupportInfo: bsupport-1.png, createBy: , createTime: 0000-00-00 00:00:00, updateBy: hambaallah, updateTime: 2020-02-09 06:32:57}]

I already read the documentation about type data that can be store in SharedPreferences, and I found can be store as List[String]. The problem is return of appInfoList above is List[dynamic], It's possible convert from List[dynamic] to List[String], so i can add it to SharedPreferences ?

My Experiment

I follow This Link and try to implement it.

I create GlobalProvider To Store Value From API.

class GlobalProvider extends ChangeNotifier {
  static const _APP_INFO_LIST123 = "appInfoList123";

  GlobalProvider() {
    syncDataWithProvider();
  }

 List<AppInfoModel> appInfoList123 = [];  


   Future updateSharedPreferencesAppInfo123(List<dynamic> value) async {
    List<String> myAppInfoList123 = value.map((e) => json.encode(e)).toList();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final result =
        await prefs.setStringList(_APP_INFO_LIST123, myAppInfoList123);
    print('result from updateSP $result');
    print('Myappinfolist123 : $myAppInfoList123');
  }

  Future syncDataWithProvider() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var result = prefs.getStringList(_APP_INFO_LIST123);
    print('The Result is $result');
    if (result != null) {
      appInfoList123 = result
          .map<AppInfoModel>((e) => AppInfoModel.fromJson(json.decode(e)))
          .toList();
    }
    print('Hore $appInfoList123');
    notifyListeners();
  }


}

Then, when I access it like this :

SplashScreen.dart

class _SplashScreenState extends State<SplashScreen> {
  GlobalProvider globalProvider;
 @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    globalProvider = Provider.of<GlobalProvider>(context);
    testing(globalProvider);
  }

  testing(GlobalProvider gp) async {
    final appInfoList = appInfoApi.getAppInfo();
    final result = await appInfoList;
    print('Hello List Dynamic From didChangeDependecies : $result');
    return await gp.updateSharedPreferencesAppInfo123(result);
  }

  @override
  Widget build(BuildContext context) {
   return Scaffold(
      body: Center(child: Text(globalProvider.appInfoList123[0].fileInfo)), <!!! IN HERE
    );
}

}

But i got this error

Error


I/flutter (26131): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (26131): The following RangeError was thrown building SplashScreen(dirty, dependencies:
I/flutter (26131): [_DefaultInheritedProviderScope<GlobalProvider>], state: _SplashScreenState#a10e3):
I/flutter (26131): RangeError (index): Invalid value: Valid value range is empty: 0
I/flutter (26131):
I/flutter (26131): The relevant error-causing widget was:
I/flutter (26131):   SplashScreen 
lib\main.dart:42
I/flutter (26131):
I/flutter (26131): When the exception was thrown, this was the stack:
I/flutter (26131): #0      List.[]  (dart:core-patch/growable_array.dart:146:60)
I/flutter (26131): #1      _SplashScreenState.build 
package:klenger_burger_app/screens/splash_screen.dart:88
I/flutter (26131): #2      StatefulElement.build 
package:flutter/…/widgets/framework.dart:4590
I/flutter (26131): #3      ComponentElement.performRebuild 
package:flutter/…/widgets/framework.dart:4478
I/flutter (26131): #4      StatefulElement.performRebuild 
package:flutter/…/widgets/framework.dart:4646
I/flutter (26131): #5      Element.rebuild 
package:flutter/…/widgets/framework.dart:4202
I/flutter (26131): #6      ComponentElement._firstBuild 
package:flutter/…/widgets/framework.dart:4461
I/flutter (26131): #7      StatefulElement._firstBuild 
package:flutter/…/widgets/framework.dart:4637
I/flutter (26131): #8      ComponentElement.mount 
package:flutter/…/widgets/framework.dart:4456
I/flutter (26131): ...     Normal element mounting (115 frames)
I/flutter (26131): #123    Element.inflateWidget 
package:flutter/…/widgets/framework.dart:3430
I/flutter (26131): #124    MultiChildRenderObjectElement.mount 
package:flutter/…/widgets/framework.dart:5857
I/flutter (26131): ...     Normal element mounting (253 frames)
I/flutter (26131): #377    Element.inflateWidget 
package:flutter/…/widgets/framework.dart:3430
I/flutter (26131): #378    Element.updateChild 
package:flutter/…/widgets/framework.dart:3198
I/flutter (26131): #379    ComponentElement.performRebuild 
package:flutter/…/widgets/framework.dart:4498
I/flutter (26131): #380    _InheritedProviderScopeMixin.performRebuild 
package:provider/src/inherited_provider.dart:220
I/flutter (26131): #381    Element.rebuild 
package:flutter/…/widgets/framework.dart:4202
I/flutter (26131): #382    ComponentElement._firstBuild 
package:flutter/…/widgets/framework.dart:4461
I/flutter (26131): #383    ComponentElement.mount 
package:flutter/…/widgets/framework.dart:4456
I/flutter (26131): ...     Normal element mounting (7 frames)
I/flutter (26131): #390    SingleChildWidgetElementMixin.mount 
package:nested/nested.dart:223
I/flutter (26131): ...     Normal element mounting (7 frames)
I/flutter (26131): #397    _NestedHookElement.mount 
package:nested/nested.dart:188
I/flutter (26131): ...     Normal element mounting (7 frames)
I/flutter (26131): #404    SingleChildWidgetElementMixin.mount 
package:nested/nested.dart:223
I/flutter (26131): #405    Element.inflateWidget 
package:flutter/…/widgets/framework.dart:3430
I/flutter (26131): #406    Element.updateChild 
package:flutter/…/widgets/framework.dart:3198
I/flutter (26131): #407    RenderObjectToWidgetElement._rebuild 
package:flutter/…/widgets/binding.dart:1142
I/flutter (26131): #408    RenderObjectToWidgetElement.mount 
package:flutter/…/widgets/binding.dart:1113
I/flutter (26131): #409    RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> 
package:flutter/…/widgets/binding.dart:1055
I/flutter (26131): #410    BuildOwner.buildScope 
package:flutter/…/widgets/framework.dart:2591
I/flutter (26131): #411    RenderObjectToWidgetAdapter.attachToRenderTree 
package:flutter/…/widgets/binding.dart:1054
I/flutter (26131): #412    WidgetsBinding.attachRootWidget 

package:flutter/…/widgets/binding.dart:935
I/flutter (26131): #413    WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> 
package:flutter/…/widgets/binding.dart:917
I/flutter (26131): (elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch)
I/flutter (26131):
I/flutter (26131): ════════════════════════════════════════════════════════════════════════════════════════════════════

Any recommendation how can i adding List[dynamic] into SharedPreferences ?

If you need My Model

class AppInfoModel {
  String kodeInfo;
  String namaInfo;
  String keteranganInfo;
  String fileInfo;
  String logoInfo;
  String loginBackgroundInfo;
  String loginLeftInfo;
  String loginSupportInfo;
  String createBy;
  String createTime;
  String updateBy;
  DateTime updateTime;

  AppInfoModel({
    this.kodeInfo,
    this.namaInfo,
    this.keteranganInfo,
    this.fileInfo,
    this.logoInfo,
    this.loginBackgroundInfo,
    this.loginLeftInfo,
    this.loginSupportInfo,
    this.createBy,
    this.createTime,
    this.updateBy,
    this.updateTime,
  });

  factory AppInfoModel.fromJson(Map<String, dynamic> json) => AppInfoModel(
        kodeInfo: json["kodeInfo"] == null ? '' : json["kodeInfo"],
        namaInfo: json["namaInfo"] == null ? '' : json["namaInfo"],
        keteranganInfo:
            json["keteranganInfo"] == null ? '' : json["keteranganInfo"],
        fileInfo: json["fileInfo"] == null ? '' : json["fileInfo"],
        logoInfo: json["logoInfo"] == null ? '' : json["logoInfo"],
        loginBackgroundInfo: json["loginBackgroundInfo"] == null
            ? ''
            : json["loginBackgroundInfo"],
        loginLeftInfo:
            json["loginLeftInfo"] == null ? '' : json["loginLeftInfo"],
        loginSupportInfo:
            json["loginSupportInfo"] == null ? '' : json["loginSupportInfo"],
        createBy: json["createBy"] == null ? '' : json["createBy"],
        createTime: json["createTime"] == null ? '' : json["createTime"],
        updateBy: json["updateBy"] == null ? '' : json["updateBy"],
        updateTime: DateTime.parse(json["updateTime"]) == null
            ? ''
            : DateTime.parse(json["updateTime"]),
      );
  Map toJson() => {
        'kodeInfo': this.kodeInfo,
        'namaInfo': this.namaInfo,
        'keteranganInfo': this.keteranganInfo,
        'fileInfo': this.fileInfo,
        'logoInfo': this.logoInfo,
        'loginBackgroundInfo': this.loginBackgroundInfo,
        'loginLeftInfo': this.loginLeftInfo,
        'loginSupportInfo': this.loginSupportInfo,
        'createBy': this.createBy,
        'createTime': this.createTime,
        'updateBy': this.updateBy,
        'updateTime': this.updateTime,
      };
}

final appInfoModel = AppInfoModel();

5
  • Did you tried json.encodeing on writing and json.deocdeing when reading? Commented Mar 3, 2020 at 18:09
  • @MisirJafarov Yes, i already try that. The problem is , when i access it in splashscreen i get error like above, but strangely if i refresh again, error still show but the value show Commented Mar 3, 2020 at 18:13
  • fyi: unlike some languages dart json decoder converts json objects to map not directly to object. So you need to map each property to you own class. Commented Mar 3, 2020 at 18:15
  • @MisirJafarov in my GlobalProvider function syncDataWithProvider i already map each property. Correct Me If i Wrong. Commented Mar 3, 2020 at 18:18
  • please check my answer Commented Mar 3, 2020 at 18:32

1 Answer 1

1

You did made a mistake. You need to use FutureBuilder, ValieListenableBuilder or something else to wait while the data is reading. Because syncDataWithProvider is asynchronous call. Which means it will not block current thread while execution is completed.

class GlobalProvider extends ChangeNotifier {
  static const _APP_INFO_LIST123 = "appInfoList123";

  GlobalProvider() {
    future = syncDataWithProvider();
  }

 List<AppInfoModel> appInfoList123 = [];  
 Future future;

 Fure updateSharedPreferencesAppInfo123(List<dynamic> value) async {
   List<String> myAppInfoList123 = value.map((e) => json.encode(e)).toList();
   SharedPreferences prefs = await SharedPreferences.getInstance();
   final result = await prefs.setStringList(_APP_INFO_LIST123, myAppInfoList123);
   print('result from updateSP $result');
   print('Myappinfolist123 : $myAppInfoList123');
 }

 Future syncDataWithProvider() async {
   SharedPreferences prefs = await SharedPreferences.getInstance();
   var result = prefs.getStringList(_APP_INFO_LIST123);
   print('The Result is $result');
   if (result != null) {
     appInfoList123 = result
       .map<AppInfoModel>((e) => AppInfoModel.fromJson(json.decode(e)))
       .toList();
   }
   print('Hore $appInfoList123');
   notifyListeners();
  }
}

And your view.dart

return FutureBuilder(
  future: globalProvider.future,
  builder: (context, snapshot) {
    if (!snapshot.hasData) {
      return Text('Loading...');
    }

    return Scaffold(
      body: Center(
        child: Text(globalProvider.appInfoList123[0].fileInfo),
      ),
    )
  }
);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.