Ricardo Rodrigues
Ricardo Rodrigues

Reputation: 482

What am I doing wrong with this BloC and SharedPreferences?

Resume of the problem: After app restart, preferences are null.

I´m building a login screen with one Settings button on the AppBar and every time I restart the app, the saved preferences appear to be "deleted". I´m using Bloc library.

I would also like to have some suggestions for code improvement :).

BloC State

import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';

@immutable
abstract class WebApiConfigsState extends Equatable {
  WebApiConfigsState([List props = const []]) : super(props);
}

//Load(ing)
class WebApiConfigsLoading extends WebApiConfigsState {
  final WebApiConfigs webApiConfigs;

  WebApiConfigsLoading({this.webApiConfigs}) : super([webApiConfigs]);

  @override
  String toString() => 'WebApiConfigsLoading';
}

//Load
class WebApiConfigsLoaded extends WebApiConfigsState {
  final WebApiConfigs webApiConfigs;

  WebApiConfigsLoaded({this.webApiConfigs}) : super([webApiConfigs]);

  @override
  String toString() => 'WebApiConfigsLoaded';
}

//Save
class WebApiConfigsSaving extends WebApiConfigsState {
  final WebApiConfigs webApiConfigs;

  WebApiConfigsSaving({this.webApiConfigs}) : super([webApiConfigs]);

  @override
  String toString() => 'WebApiConfigsSaving';
}

//Save(d)
class WebApiConfigsSaved extends WebApiConfigsState {
  final WebApiConfigs webApiConfigs;

  WebApiConfigsSaved({this.webApiConfigs}) : super([webApiConfigs]);

  @override
  String toString() => 'WebApiConfigsSaving';
}

//Erro
class WebApiConfigsError extends WebApiConfigsState {
  final String error;

  WebApiConfigsError({@required this.error}) : super([error]);

  @override
  String toString() => 'WebApiConfigsError { error: $error }';
}

BloC Event

import 'package:equatable/equatable.dart';

import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';

abstract class WebApiConfigsEvent extends Equatable {}

//Load
class LoadWebApiConfigsEvent extends WebApiConfigsEvent {
  WebApiConfigs webApiConfigs;

  LoadWebApiConfigsEvent(this.webApiConfigs);

  @override
  String toString() => 'LoadWebApiConfigsEvent';
}

//Save
class SaveWebApiConfigsEvent extends WebApiConfigsEvent {
  final WebApiConfigs webApiConfigs;

  SaveWebApiConfigsEvent(this.webApiConfigs);

  @override
  String toString() => 'SaveWebApiConfigsEvent { URL: ${webApiConfigs.url} }';
}

BloC Bloc

import 'package:bloc/bloc.dart';

import 'package:visiogate/vg_blocs/webApiConfigs/webApiConfigs.dart';
import 'package:visiogate/vg_blocs/simple_bloc_delegate.dart';
import 'package:visiogate/vg_models/persistent_data/persistent_data.dart';
import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();
}

class WebApiConfigsBloc extends Bloc<WebApiConfigsEvent, WebApiConfigsState> {
  PersistentData _persistentData = new PersistentData();
  WebApiConfigs _wac = new WebApiConfigs("", "", "", "", "", "");

  @override
  WebApiConfigsState get initialState =>
      WebApiConfigsLoading(webApiConfigs: _wac);

  @override
  Stream<WebApiConfigsState> mapEventToState(
    WebApiConfigsEvent event,
  ) async* {
    //Load
    if (event is LoadWebApiConfigsEvent) {
      try {
        final WebApiConfigs _wac = await _persistentData.getApiConfigs();
        yield WebApiConfigsLoaded(webApiConfigs: _wac);
      } catch (error) {
        yield WebApiConfigsError(error: error.toString());
      }
    }
    //Save
    if (event is SaveWebApiConfigsEvent) {
      try {
        yield WebApiConfigsSaving(webApiConfigs: event.webApiConfigs);
        yield WebApiConfigsSaved(webApiConfigs: event.webApiConfigs);
      } catch (error) {
        yield WebApiConfigsError(error: error.toString());
      }
    }
  }
}

The Form

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:visiogate/vg_blocs/webApiConfigs/webApiConfigs.dart';
import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';

class ConfigsForm extends StatefulWidget {
  final WebApiConfigsBloc webApiConfigsBloc;

  const ConfigsForm({Key key, @required this.webApiConfigsBloc})
      : super(key: key);

  @override
  _ConfigsFormState createState() => _ConfigsFormState();
}

class _ConfigsFormState extends State<ConfigsForm> {
  final _urlController = TextEditingController();
  final _companyController = TextEditingController();

  WebApiConfigsBloc get _webApiConfigsBloc => widget.webApiConfigsBloc;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<WebApiConfigsEvent, WebApiConfigsState>(
      bloc: _webApiConfigsBloc,
      builder: (
        BuildContext context,
        WebApiConfigsState state,
      ) {
        if (state is WebApiConfigsError) {
          _onWidgetDidBuild(() {
            Scaffold.of(context).showSnackBar(
              SnackBar(
                content: Text('${state.error}'),
                backgroundColor: Colors.red,
              ),
            );
          });
        } else if (state is WebApiConfigsLoading) {
          _webApiConfigsBloc
              .dispatch(LoadWebApiConfigsEvent(state.webApiConfigs));
        } else if (state is WebApiConfigsLoaded) {
          _urlController.text = state.webApiConfigs.url;
          _companyController.text = state.webApiConfigs.company;
          //TODO: showSnackBar
        } else if (state is WebApiConfigsSaved) {
          _urlController.text = state.webApiConfigs.url;
          _companyController.text = state.webApiConfigs.company;
          //TODO: showSnackBar
        }

        return Column(
          children: <Widget>[
            Form(
              child: Column(
                children: [
                  TextFormField(
                    decoration: InputDecoration(labelText: 'url'),
                    controller: _urlController,
                  ),
                  TextFormField(
                    decoration: InputDecoration(labelText: 'company'),
                    controller: _companyController,
                  ),
                  RaisedButton(
                    onPressed: state is! WebApiConfigsLoading
                        ? _onSaveConfigsButtonPressed
                        : null,
                    child: Text('save'),
                  ),
                  Container(
                    child: state is WebApiConfigsLoading
                        ? CircularProgressIndicator()
                        : null,
                  ),
                ],
              ),
            ),
          ],
        );
      },
    );
  }

  void _onWidgetDidBuild(Function callback) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      callback();
    });
  }

  _onSaveConfigsButtonPressed() {
    //TODO: fazer restantes controlos
    WebApiConfigs _wac = new WebApiConfigs(
        _urlController.text,
        _companyController.text,
        "default",
        "bearer",
        "professional",
        "password");

    _webApiConfigsBloc.dispatch(SaveWebApiConfigsEvent(
      _wac,
    ));
  }
}

SharedPreferences

import 'package:shared_preferences/shared_preferences.dart';

import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';
import 'package:visiogate/vg_models/token/token.dart';

class PersistentData {
  String _apiUrl = "api_url";
  String _apiCompany = "api_company";
  String _apiInstance = "api_instance";
  String _apiAuthorization = "api_authorization";
  String _apiLine = "api_line";
  String _apiGrantType = "api_grant_type";

  Future<void> setApiConfigs(WebApiConfigs webapi) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(_apiUrl, webapi.url);
    prefs.setString(_apiCompany, webapi.company);
    prefs.setString(_apiInstance, webapi.instance);
    prefs.setString(_apiAuthorization, webapi.authorization);
    prefs.setString(_apiLine, webapi.line);
    prefs.setString(_apiGrantType, webapi.grantType);
    return;
  }

  Future<WebApiConfigs> getApiConfigs() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String _ur = prefs.getString(_apiUrl) ?? "";
    String _co = prefs.getString(_apiCompany) ?? "";
    String _ins = prefs.getString(_apiInstance) ?? "D";
    String _au = prefs.getString(_apiAuthorization) ?? "bearer";
    String _ln = prefs.getString(_apiLine) ?? "pro";
    String _gt = prefs.getString(_apiGrantType) ?? "pwd";

    WebApiConfigs webapi = new WebApiConfigs(_ur, _co, _ins, _au, _ln, _gt);
    return webapi;
  }
}

If I do not restart the app, all the "saved" configs are there! What am I doing wrong? It´s probably just a simple line but...it´s driving me crazy!

TY in andvance.

Upvotes: 2

Views: 2254

Answers (1)

Ricardo Rodrigues
Ricardo Rodrigues

Reputation: 482

After some coffee and fresh air on my face…

The problem was on the Form and on the Bloc. Here is the code so that no other soul gets crazy and also my contribution to the community:

BloC Bloc

import 'package:bloc/bloc.dart';

import 'package:visiogate/vg_blocs/webApiConfigs/webApiConfigs.dart';
import 'package:visiogate/vg_blocs/simple_bloc_delegate.dart';
import 'package:visiogate/vg_models/persistent_data/persistent_data.dart';
import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();
}

class WebApiConfigsBloc extends Bloc<WebApiConfigsEvent, WebApiConfigsState> {
  PersistentData _persistentData = new PersistentData();
  WebApiConfigs _wac = new WebApiConfigs("", "", "", "", "", "");

  @override
  WebApiConfigsState get initialState =>
      WebApiConfigsLoading(webApiConfigs: _wac);

  @override
  Stream<WebApiConfigsState> mapEventToState(
    WebApiConfigsEvent event,
  ) async* {
    //Load
    if (event is LoadWebApiConfigsEvent) {
      try {
        event.webApiConfigs = await _persistentData.getApiConfigs();
        yield WebApiConfigsLoaded(webApiConfigs: event.webApiConfigs);
      } catch (error) {
        yield WebApiConfigsError(error: error.toString());
      }
    }
    //Save
    if (event is SaveWebApiConfigsEvent) {
      try {
        _persistentData.setApiConfigs(event.webApiConfigs);
        yield WebApiConfigsSaving(webApiConfigs: event.webApiConfigs);
        yield WebApiConfigsLoaded(webApiConfigs: event.webApiConfigs);
      } catch (error) {
        yield WebApiConfigsError(error: error.toString());
      }
    }
  }
}

Form

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:visiogate/vg_blocs/webApiConfigs/webApiConfigs.dart';
import 'package:visiogate/vg_models/webApiConfigs/webapi_model.dart';

class ConfigsForm extends StatefulWidget {
  final WebApiConfigsBloc webApiConfigsBloc;

  const ConfigsForm({Key key, @required this.webApiConfigsBloc})
      : super(key: key);

  @override
  _ConfigsFormState createState() => _ConfigsFormState();
}

class _ConfigsFormState extends State<ConfigsForm> {
  final _urlController = TextEditingController();
  final _companyController = TextEditingController();

  WebApiConfigsBloc get _webApiConfigsBloc => widget.webApiConfigsBloc;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<WebApiConfigsEvent, WebApiConfigsState>(
      bloc: _webApiConfigsBloc,
      builder: (
        BuildContext context,
        WebApiConfigsState state,
      ) {
        if (state is WebApiConfigsError) {
          _onWidgetDidBuild(() {
            Scaffold.of(context).showSnackBar(
              SnackBar(
                content: Text('${state.error}'),
                backgroundColor: Colors.red,
              ),
            );
          });
        } else if (state is WebApiConfigsLoading) {
          _webApiConfigsBloc
              .dispatch(LoadWebApiConfigsEvent(state.webApiConfigs));
        } else if (state is WebApiConfigsSaving) {
          _urlController.text = state.webApiConfigs.url;
          _companyController.text = state.webApiConfigs.company;
          //TODO: showSnackBar
        } else if (state is WebApiConfigsLoaded) {
          _urlController.text = state.webApiConfigs.url;
          _companyController.text = state.webApiConfigs.company;
          //TODO: showSnackBar
        }

        return Column(
          children: <Widget>[
            Form(
              child: Column(
                children: [
                  TextFormField(
                    decoration: InputDecoration(labelText: 'url'),
                    controller: _urlController,
                  ),
                  TextFormField(
                    decoration: InputDecoration(labelText: 'company'),
                    controller: _companyController,
                  ),
                  RaisedButton(
                    onPressed: (state is! WebApiConfigsLoading)
                        ? _onSaveConfigsButtonPressed
                        : null,
                    child: Text('save'),
                  ),
                  Container(
                    child: (state is WebApiConfigsLoading)
                        ? CircularProgressIndicator()
                        : null,
                  ),
                ],
              ),
            ),
          ],
        );
      },
    );
  }

  void _onWidgetDidBuild(Function callback) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      callback();
    });
  }

  _onSaveConfigsButtonPressed() {
    WebApiConfigs _wac = new WebApiConfigs(
        _urlController.text,
        _companyController.text,
        "d",
        "bearer",
        "pro",
        "pwd");

    _webApiConfigsBloc.dispatch(SaveWebApiConfigsEvent(
      _wac,
    ));
  }
}

The main issue was that I was using "_wac" variable instead of "event.webApiConfigs" on the BloC. Also removed the State "WebApiConfigsSaved" because it has the same final has "WebApiConfigsLoaded".

Cheers.

Upvotes: 1

Related Questions