From 441d4070050f0525d7bfb3da68627a72776b7a76 Mon Sep 17 00:00:00 2001 From: Daniel Borges Date: Wed, 14 Aug 2019 11:09:05 -0300 Subject: [PATCH] BLoC pattern in settings page --- lib/main.dart | 7 +- lib/src/bloc/settings_bloc.dart | 139 +++++++++++++++++++ lib/src/screens/settings.dart | 230 ++++++++++++++++---------------- pubspec.yaml | 3 + 4 files changed, 266 insertions(+), 113 deletions(-) create mode 100644 lib/src/bloc/settings_bloc.dart diff --git a/lib/main.dart b/lib/main.dart index 18433dc..0650fc2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,8 +21,10 @@ // DEALINGS IN THE SOFTWARE. import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'src/bloc/settings_bloc.dart'; import 'src/openlp_mobile_remote_app.dart'; import 'src/configurations/app_theme.dart'; import 'src/configurations/app_localizations.dart'; @@ -60,7 +62,10 @@ void main() => runApp( }, routes: { '/': (context) => OpenLPMobileRemoteApp(), - '/settings': (context) => Settings(), + '/settings': (context) => BlocProvider( + builder: (context) => SettingsBloc(), + child: Settings(), + ), }, ), ); diff --git a/lib/src/bloc/settings_bloc.dart b/lib/src/bloc/settings_bloc.dart new file mode 100644 index 0000000..2d43f6b --- /dev/null +++ b/lib/src/bloc/settings_bloc.dart @@ -0,0 +1,139 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; + +class SettingsState { + String serverIp; + int serverPort; + bool useHttps; + bool needsAuth; + String userId; + String userPassword; + + SettingsState({ + @required this.serverIp, + @required this.serverPort, + @required this.useHttps, + @required this.needsAuth, + @required this.userId, + @required this.userPassword, + }); + + SettingsState copyWith({ + String serverIp, + int serverPort, + bool useHttps, + bool needsAuth, + String userId, + String userPassword, + }) { + return SettingsState( + serverIp: serverIp ?? this.serverIp, + serverPort: serverPort ?? this.serverPort, + useHttps: useHttps ?? this.useHttps, + needsAuth: needsAuth ?? this.needsAuth, + userId: userId ?? this.userId, + userPassword: userPassword ?? this.userPassword, + ); + } +} + +abstract class SettingsEvent extends Equatable { + final T value; + SettingsEvent(this.value, [List props = const []]) : super(props); +} + +class SetServerIpEvent extends SettingsEvent { + SetServerIpEvent(String value) : super(value); +} + +class SetServerPortEvent extends SettingsEvent { + SetServerPortEvent(int value) : super(value); +} + +class SetUseHttpsEvent extends SettingsEvent { + SetUseHttpsEvent(bool value) : super(value); +} + +class SetNeedsAuthEvent extends SettingsEvent { + SetNeedsAuthEvent(bool value) : super(value); +} + +class SetUserIdEvent extends SettingsEvent { + SetUserIdEvent(String value) : super(value); +} + +class SetUserPasswordEvent extends SettingsEvent { + SetUserPasswordEvent(String value) : super(value); +} + +class SettingsBloc extends Bloc { + @override + SettingsState get initialState => SettingsState( + serverIp: '192.168.1.100', + serverPort: 4316, + useHttps: false, + needsAuth: false, + userId: 'openlp', + userPassword: 'password', + ); + + void setServerIp(String serverIp) { + if (serverIp == null || serverIp.isEmpty) { + return; + } + dispatch(SetServerIpEvent(serverIp)); + } + + void setServerPort(String serverPortStr) { + if (serverPortStr == null || serverPortStr.isEmpty) { + return; + } + int serverPort = int.tryParse(serverPortStr); + dispatch(SetServerPortEvent(serverPort)); + } + + void setUseHttps(bool useHttps) { + dispatch(SetUseHttpsEvent(useHttps)); + } + + void setNeedsAuth(bool needsAuth) { + dispatch(SetNeedsAuthEvent(needsAuth)); + } + + void setUserId(String userId) { + if (userId == null || userId.isEmpty) { + return; + } + dispatch(SetUserIdEvent(userId)); + } + + void setUserPassword(String userPassword) { + if (userPassword == null || userPassword.isEmpty) { + return; + } + dispatch(SetUserPasswordEvent(userPassword)); + } + + @override + Stream mapEventToState(SettingsEvent event) async* { + if (event is SetServerIpEvent) { + yield this.currentState.copyWith(serverIp: event.value); + } + if (event is SetServerPortEvent) { + yield this.currentState.copyWith(serverPort: event.value); + } + if (event is SetUseHttpsEvent) { + yield this.currentState.copyWith(useHttps: event.value); + } + if (event is SetNeedsAuthEvent) { + yield this.currentState.copyWith(needsAuth: event.value); + } + if (event is SetUserIdEvent) { + yield this.currentState.copyWith(userId: event.value); + } + if (event is SetUserPasswordEvent) { + yield this.currentState.copyWith(userPassword: event.value); + } + } +} diff --git a/lib/src/screens/settings.dart b/lib/src/screens/settings.dart index a16f93c..2d1e2e2 100644 --- a/lib/src/screens/settings.dart +++ b/lib/src/screens/settings.dart @@ -21,122 +21,119 @@ // DEALINGS IN THE SOFTWARE. import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../bloc/settings_bloc.dart'; import '../configurations/app_localizations.dart'; -class Settings extends StatefulWidget { - @override - State createState() => _SettingState(); -} - -class _SettingState extends State { - String serverIp; - int serverPort; - bool useHttps; - bool needsAuth; - String userId; - String userPassword; - - @override - void initState() { - super.initState(); - serverIp = '192.168.1.100'; - serverPort = 4316; - useHttps = false; - needsAuth = false; - userId = 'openlp'; - userPassword = 'password'; - } - +class Settings extends StatelessWidget { @override Widget build(BuildContext context) { + final SettingsBloc bloc = BlocProvider.of(context); return Scaffold( appBar: AppBar( title: Text(AppLocalizations.of(context).translate('settings')), ), - body: ListView( - children: [ - ListTile( - title: Text( - AppLocalizations.of(context).translate('settings_server_ip')), - subtitle: Text(serverIp), - onTap: () { - showDialog( - context: context, - builder: (context) => _InputDialog(AppLocalizations.of(context) - .translate('dialog_server_ip_title')), - ); - }, - ), - ListTile( - title: Text( - AppLocalizations.of(context).translate('settings_server_port')), - subtitle: Text('$serverPort'), - onTap: () { - showDialog( - context: context, - builder: (context) => _InputDialog(AppLocalizations.of(context) - .translate('dialog_server_port_title')), - ); - }, - ), - CheckboxListTile( - title: Text( - AppLocalizations.of(context).translate('settings_use_https')), - onChanged: (value) { - setState(() { - useHttps = value; - }); - }, - value: useHttps, - ), - Divider(), - CheckboxListTile( - title: Text( - AppLocalizations.of(context).translate('settings_needs_auth')), - onChanged: (value) { - setState(() { - needsAuth = value; - }); - }, - value: needsAuth, - ), - ListTile( - enabled: needsAuth, - title: Text( - AppLocalizations.of(context).translate('settings_user_id')), - subtitle: Text(userId), - onTap: () { - showDialog( - context: context, - builder: (context) => _InputDialog(AppLocalizations.of(context) - .translate('dialog_server_user_id_title')), - ); - }, - ), - ListTile( - enabled: needsAuth, - title: Text( - AppLocalizations.of(context).translate('settings_user_pass')), - subtitle: Text(userPassword), - onTap: () { - showDialog( - context: context, - builder: (context) => _InputDialog(AppLocalizations.of(context) - .translate('dialog_server_user_pass_title')), - ); - }, - ), - Divider(), - ListTile( - title: Text(AppLocalizations.of(context) - .translate('settings_about_openlp')), - onTap: () { - launch('https://openlp.org/'); - }, - ), - ], + body: BlocBuilder( + builder: (context, state) => ListView( + children: [ + ListTile( + title: Text( + AppLocalizations.of(context).translate('settings_server_ip')), + subtitle: Text(state.serverIp ?? ''), + onTap: () async { + String title = AppLocalizations.of(context) + .translate('dialog_server_ip_title'); + String serverIp = await showDialog( + context: context, + builder: (context) { + return _InputDialog(title, + initialValue: state.serverIp, + keyboardType: TextInputType.url); + }, + ); + bloc.setServerIp(serverIp); + }, + ), + ListTile( + title: Text(AppLocalizations.of(context) + .translate('settings_server_port')), + subtitle: Text('${state.serverPort}'), + onTap: () async { + String title = AppLocalizations.of(context) + .translate('dialog_server_port_title'); + String serverPortStr = await showDialog( + context: context, + builder: (context) { + return _InputDialog(title, + initialValue: '${state.serverPort}', + keyboardType: TextInputType.numberWithOptions( + decimal: true, signed: true)); + }, + ); + bloc.setServerPort(serverPortStr); + }, + ), + CheckboxListTile( + title: Text( + AppLocalizations.of(context).translate('settings_use_https')), + value: state.useHttps, + onChanged: (value) { + bloc.setUseHttps(value); + }, + ), + Divider(), + CheckboxListTile( + title: Text(AppLocalizations.of(context) + .translate('settings_needs_auth')), + value: state.needsAuth, + onChanged: (value) { + bloc.setNeedsAuth(value); + }, + ), + ListTile( + enabled: state.needsAuth, + title: Text( + AppLocalizations.of(context).translate('settings_user_id')), + subtitle: Text(state.userId ?? ''), + onTap: () async { + String title = AppLocalizations.of(context) + .translate('dialog_server_user_id_title'); + String userId = await showDialog( + context: context, + builder: (context) => + _InputDialog(title, initialValue: state.userId), + ); + bloc.setUserId(userId); + }, + ), + ListTile( + enabled: state.needsAuth, + title: Text( + AppLocalizations.of(context).translate('settings_user_pass')), + subtitle: Text(state.userPassword ?? ''), + onTap: () async { + String title = AppLocalizations.of(context) + .translate('dialog_server_user_pass_title'); + String userPassword = await showDialog( + context: context, + builder: (context) => + _InputDialog(title, initialValue: state.userPassword), + ); + bloc.setUserPassword(userPassword); + }, + ), + Divider(), + ListTile( + title: Text(AppLocalizations.of(context) + .translate('settings_about_openlp')), + onTap: () { + launch('https://openlp.org/'); + }, + ), + ], + ), ), ); } @@ -144,27 +141,36 @@ class _SettingState extends State { class _InputDialog extends StatelessWidget { final String title; + final String initialValue; + final TextInputType keyboardType; + final TextEditingController _textEditingController; - _InputDialog(this.title); + _InputDialog( + this.title, { + this.initialValue, + this.keyboardType = TextInputType.text, + }) : _textEditingController = TextEditingController(text: initialValue ?? ''); @override Widget build(BuildContext context) { return AlertDialog( title: Text(this.title), contentPadding: EdgeInsets.all(15), - content: TextField(autofocus: true), + content: TextField( + autofocus: true, + controller: _textEditingController, + keyboardType: keyboardType, + ), actions: [ FlatButton( child: Text(AppLocalizations.of(context).translate('button_cancel')), onPressed: () { - Navigator.pop(context); + Navigator.pop(context, ''); }, ), FlatButton( child: Text(AppLocalizations.of(context).translate('button_ok')), - onPressed: () { - Navigator.pop(context, ''); - }, + onPressed: () => Navigator.pop(context, _textEditingController.text), ), ], ); diff --git a/pubspec.yaml b/pubspec.yaml index 6d35229..118ec49 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,9 @@ dependencies: url_launcher: ^5.1.2 flutter_localizations: sdk: flutter + bloc: ^0.14.0 + flutter_bloc: ^0.20.0 + equatable: ^0.4.0 dev_dependencies: flutter_test: