From 50bc599feeca3cd6fefcfd6a4953afc6308f1703 Mon Sep 17 00:00:00 2001 From: Santiago Garcia Gil Date: Thu, 6 Feb 2025 14:49:23 -0500 Subject: [PATCH] feat: improve example --- .../lib/application/app_config.dart | 25 ++-- .../lib/async_client_service.dart | 53 +++++++-- .../model/gateway/async_client_gateway.dart | 5 +- .../driven_adapter/api_service.dart | 8 +- .../notifier/log_notifier.dart | 9 ++ examples/app_async_flutter/lib/main.dart | 5 +- examples/app_async_flutter/lib/my_app.dart | 4 +- examples/app_async_flutter/lib/setup.dart | 49 ++++++-- .../lib/ui/atoms/delay_field.dart | 23 ---- .../lib/ui/atoms/input_field.dart | 29 +++++ .../lib/ui/helpers/home_helper.dart | 15 ++- .../lib/ui/pages/config_page.dart | 102 ++++++++++++++++ .../lib/ui/pages/home_page.dart | 109 ++++++------------ .../lib/ui/pages/log_viewer.dart | 44 ------- .../lib/ui/pages/log_viewer_page.dart | 66 +++++++++++ .../lib/ui/pages/request_page.dart | 81 +++++++++++++ examples/app_async_flutter/pubspec.lock | 16 +-- examples/app_async_flutter/pubspec.yaml | 1 + 18 files changed, 455 insertions(+), 189 deletions(-) rename examples/app_async_flutter/lib/{infraestructure => infrastructure}/driven_adapter/api_service.dart (77%) rename examples/app_async_flutter/lib/{infraestructure => infrastructure}/notifier/log_notifier.dart (67%) delete mode 100644 examples/app_async_flutter/lib/ui/atoms/delay_field.dart create mode 100644 examples/app_async_flutter/lib/ui/atoms/input_field.dart create mode 100644 examples/app_async_flutter/lib/ui/pages/config_page.dart delete mode 100644 examples/app_async_flutter/lib/ui/pages/log_viewer.dart create mode 100644 examples/app_async_flutter/lib/ui/pages/log_viewer_page.dart create mode 100644 examples/app_async_flutter/lib/ui/pages/request_page.dart diff --git a/examples/app_async_flutter/lib/application/app_config.dart b/examples/app_async_flutter/lib/application/app_config.dart index 0c9bdca..8ac7ab8 100644 --- a/examples/app_async_flutter/lib/application/app_config.dart +++ b/examples/app_async_flutter/lib/application/app_config.dart @@ -1,8 +1,8 @@ -import 'package:app_async_flutter/infraestructure/notifier/log_notifier.dart'; +import 'package:app_async_flutter/infrastructure/notifier/log_notifier.dart'; import 'package:flutter/material.dart'; class AppConfig extends InheritedWidget { - const AppConfig({ + AppConfig({ Key? key, required this.businessUrl, required this.socketUrl, @@ -12,15 +12,26 @@ class AppConfig extends InheritedWidget { required Widget child, }) : super(key: key, child: child); - final String businessUrl; - final String socketUrl; - final int heartbeatInterval; - final int maxRetries; - final LogNotifier logNotifier; + String businessUrl; + String socketUrl; + int heartbeatInterval; + int maxRetries; + LogNotifier logNotifier; static AppConfig of(BuildContext context) => context.findAncestorWidgetOfExactType()!; @override bool updateShouldNotify(InheritedWidget oldWidget) => false; + + void updateConfig( + {required int heartbeatInterval, + required int maxRetries, + required String socketUrl, + required String businessUrl}) { + this.heartbeatInterval = heartbeatInterval; + this.maxRetries = maxRetries; + this.socketUrl = socketUrl; + this.businessUrl = businessUrl; + } } diff --git a/examples/app_async_flutter/lib/async_client_service.dart b/examples/app_async_flutter/lib/async_client_service.dart index ccd57ed..2b33efa 100644 --- a/examples/app_async_flutter/lib/async_client_service.dart +++ b/examples/app_async_flutter/lib/async_client_service.dart @@ -3,7 +3,9 @@ import 'package:app_async_flutter/domain/model/channel_credentials.dart'; import 'package:app_async_flutter/domain/model/gateway/async_client_gateway.dart'; import 'package:channel_sender_client/channel_sender_client.dart'; import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uuid/uuid.dart'; class ResponsesNotifier extends ChangeNotifier { List responses = []; @@ -33,21 +35,30 @@ class AsyncClientService extends InheritedWidget { final String eventListen; late AsyncClient asyncClient; final AsyncClientGateway asyncClientGateway; + final _log = Logger('AsyncClientService'); late SharedPreferences prefs; ResponsesNotifier responsesNotifier = ResponsesNotifier(); final AppConfig appConfig; - void _handleEvent(dynamic result) { - responsesNotifier.addResponse( - "Message from async dataflow, payload: ${result.payload} correlationId: ${result.correlationId}"); + void _handleEvent(dynamic msg) { + if (msg.event == 'businessEvent') { + responsesNotifier.addResponse( + 'Message from async dataflow, title: ${msg.payload['title']} detail: ${msg.payload['detail']}'); + } + + if (msg.event == 'ch-ms-async-callback.svp.reply') { + responsesNotifier.addResponse( + 'Message from async dataflow, title: ${msg.payload['data']['reply']['messageData']['title']} detail: ${msg.payload['data']['reply']['messageData']['detail']}'); + } } static AsyncClientService? of(BuildContext context) { return context.findAncestorWidgetOfExactType(); } - void closeSession() async { + Future closeSession() async { + prefs = await SharedPreferences.getInstance(); await deleteChannelCreated(); asyncClient.disconnect(); } @@ -55,11 +66,39 @@ class AsyncClientService extends InheritedWidget { Future deleteChannelCreated() async { await prefs.remove('channelRef'); await prefs.remove('channelSecret'); + await prefs.remove('userRef'); + } + + bool hasUserRef() { + return prefs.getString('userRef') != null; + } + + Future createUserRef() async { + if (hasUserRef()) { + return prefs.getString('userRef')!; + } + var uuid = const Uuid(); + String ref = uuid.v4(); + await prefs.setString('userRef', ref); + return ref; + } + + Future saveConfig() async { + prefs = await SharedPreferences.getInstance(); + + await prefs.setString('socketUrl', appConfig.socketUrl); + await prefs.setString('apiBusiness', appConfig.businessUrl); + await prefs.setString( + 'heartbeatInterval', appConfig.heartbeatInterval.toString()); + await prefs.setString('maxRetries', appConfig.maxRetries.toString()); } Future initAsyncClient() async { prefs = await SharedPreferences.getInstance(); - ChannelCredential? channelCredential = await _requestChannelCredentials(); + var userRef = await createUserRef(); + _log.info("userRef $userRef"); + ChannelCredential? channelCredential = + await _requestChannelCredentials(userRef); if (channelCredential != null) { final conf = AsyncConfig( socketUrl: appConfig.socketUrl, @@ -80,12 +119,12 @@ class AsyncClientService extends InheritedWidget { } } - Future _requestChannelCredentials() async { + Future _requestChannelCredentials(String userRef) async { ChannelCredential? channelCredential; if (hasChannelCreated()) { return getChannelCreated(); } - channelCredential = await asyncClientGateway.getCredentials(); + channelCredential = await asyncClientGateway.getCredentials(userRef); print(channelCredential!.channelRef); persistCredentials(channelCredential); return channelCredential; diff --git a/examples/app_async_flutter/lib/domain/model/gateway/async_client_gateway.dart b/examples/app_async_flutter/lib/domain/model/gateway/async_client_gateway.dart index df48359..4ff8ed7 100644 --- a/examples/app_async_flutter/lib/domain/model/gateway/async_client_gateway.dart +++ b/examples/app_async_flutter/lib/domain/model/gateway/async_client_gateway.dart @@ -1,6 +1,7 @@ import 'package:app_async_flutter/domain/model/channel_credentials.dart'; abstract class AsyncClientGateway { - Future getCredentials(); - Future callBusinessUseCase(String channelRef, int delay); + Future getCredentials(String userRef); + Future callBusinessUseCase( + String channelRef, String userRef, int delay); } diff --git a/examples/app_async_flutter/lib/infraestructure/driven_adapter/api_service.dart b/examples/app_async_flutter/lib/infrastructure/driven_adapter/api_service.dart similarity index 77% rename from examples/app_async_flutter/lib/infraestructure/driven_adapter/api_service.dart rename to examples/app_async_flutter/lib/infrastructure/driven_adapter/api_service.dart index 07fd325..c858167 100644 --- a/examples/app_async_flutter/lib/infraestructure/driven_adapter/api_service.dart +++ b/examples/app_async_flutter/lib/infrastructure/driven_adapter/api_service.dart @@ -12,10 +12,10 @@ class ApiService implements AsyncClientGateway { urlBusinessService = AppConfig.of(context).businessUrl; } @override - Future getCredentials() async { + Future getCredentials(String userRef) async { ChannelCredential? channelCredential; return http - .get(Uri.parse("$urlBusinessService/credentials")) + .get(Uri.parse("$urlBusinessService/credentials?user_ref=$userRef")) .then((response) { try { print("response.body ${response.body}"); @@ -31,8 +31,8 @@ class ApiService implements AsyncClientGateway { @override Future callBusinessUseCase( - String channelRef, int delay) async { + String channelRef, String userRef, int delay) async { return http.get(Uri.parse( - "$urlBusinessService/business?channel_ref=$channelRef&delay=$delay")); + "$urlBusinessService/business?channel_ref=$channelRef&user_ref=$userRef&delay=$delay")); } } diff --git a/examples/app_async_flutter/lib/infraestructure/notifier/log_notifier.dart b/examples/app_async_flutter/lib/infrastructure/notifier/log_notifier.dart similarity index 67% rename from examples/app_async_flutter/lib/infraestructure/notifier/log_notifier.dart rename to examples/app_async_flutter/lib/infrastructure/notifier/log_notifier.dart index 251db8d..03a11d1 100644 --- a/examples/app_async_flutter/lib/infraestructure/notifier/log_notifier.dart +++ b/examples/app_async_flutter/lib/infrastructure/notifier/log_notifier.dart @@ -1,12 +1,19 @@ import 'package:flutter/material.dart'; class LogNotifier extends ChangeNotifier { + LogLevel level = LogLevel.all; + List logs = []; void setLog(log) { logs.insert(0, log); notifyListeners(); } + void setLevel(newLevel) { + level = newLevel; + notifyListeners(); + } + void clean() { logs.clear(); notifyListeners(); @@ -16,3 +23,5 @@ class LogNotifier extends ChangeNotifier { return logs; } } + +enum LogLevel { info, all } diff --git a/examples/app_async_flutter/lib/main.dart b/examples/app_async_flutter/lib/main.dart index 1ab2ea8..878d6cb 100644 --- a/examples/app_async_flutter/lib/main.dart +++ b/examples/app_async_flutter/lib/main.dart @@ -1,9 +1,12 @@ import 'package:app_async_flutter/setup.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:shared_preferences/shared_preferences.dart'; void main() async { await dotenv.load(fileName: ".env"); WidgetsFlutterBinding.ensureInitialized(); - runApp(Setup.getApp()); + SharedPreferences prefs = await SharedPreferences.getInstance(); + + runApp(Setup.getApp(prefs)); } diff --git a/examples/app_async_flutter/lib/my_app.dart b/examples/app_async_flutter/lib/my_app.dart index 324382f..42e403d 100644 --- a/examples/app_async_flutter/lib/my_app.dart +++ b/examples/app_async_flutter/lib/my_app.dart @@ -1,6 +1,6 @@ import 'package:app_async_flutter/application/app_config.dart'; import 'package:app_async_flutter/async_client_service.dart'; -import 'package:app_async_flutter/infraestructure/driven_adapter/api_service.dart'; +import 'package:app_async_flutter/infrastructure/driven_adapter/api_service.dart'; import 'package:app_async_flutter/ui/pages/home_page.dart'; import 'package:flutter/material.dart'; @@ -11,7 +11,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return AsyncClientService( - eventListen: "businessEvent", + eventListen: "ch-ms-async-callback.svp.reply", asyncClientGateway: ApiService(context), appConfig: AppConfig.of(context), child: MaterialApp( diff --git a/examples/app_async_flutter/lib/setup.dart b/examples/app_async_flutter/lib/setup.dart index 09122ee..cc071c8 100644 --- a/examples/app_async_flutter/lib/setup.dart +++ b/examples/app_async_flutter/lib/setup.dart @@ -1,26 +1,51 @@ import 'package:app_async_flutter/application/app_config.dart'; -import 'package:app_async_flutter/infraestructure/notifier/log_notifier.dart'; +import 'package:app_async_flutter/infrastructure/notifier/log_notifier.dart'; import 'package:app_async_flutter/my_app.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:logging/logging.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class Setup { - static AppConfig getApp() { + static AppConfig getApp(SharedPreferences prefs) { var env = dotenv.env; - Logger.root.level = Level.INFO; + LogNotifier logNotifier = configureLogger(); + prefs.getString('apiBusiness'); + return AppConfig( + businessUrl: getEnvironment(prefs, 'apiBusiness'), + heartbeatInterval: int.parse(getEnvironment(prefs, 'heartbeatInterval')), + maxRetries: int.parse(getEnvironment(prefs, 'maxRetries')), + socketUrl: getEnvironment(prefs, 'socketUrl'), + logNotifier: logNotifier, + child: const MyApp(), + ); + } + + static String getEnvironment( + SharedPreferences prefs, + String key, + ) { + var businessUrl = prefs.getString(key); + if (businessUrl == null) { + businessUrl = dotenv.env[key]!; + prefs.setString(key, businessUrl); + } + print(businessUrl); + + return businessUrl; + } + + static LogNotifier configureLogger() { LogNotifier logNotifier = LogNotifier(); + logNotifier.addListener(() { + Logger.root.level = + logNotifier.level == LogLevel.all ? Level.FINEST : Level.INFO; + }); + Logger.root.onRecord.listen((record) { var log = '${record.level.name}: ${record.time}: ${record.message}'; logNotifier.setLog(log); - print(log); + // debugPrint(log); }); - return AppConfig( - businessUrl: env['apiBusiness'] ?? 'http://localhost:8080/api', - heartbeatInterval: int.parse(env['heartbeatInterval'] ?? '2500'), - maxRetries: int.parse(env['maxRetries'] ?? '15'), - socketUrl: dotenv.env['socketUrl'] ?? 'ws://localhost:8082/ext/socket', - logNotifier: logNotifier, - child: const MyApp(), - ); + return logNotifier; } } diff --git a/examples/app_async_flutter/lib/ui/atoms/delay_field.dart b/examples/app_async_flutter/lib/ui/atoms/delay_field.dart deleted file mode 100644 index 608c78e..0000000 --- a/examples/app_async_flutter/lib/ui/atoms/delay_field.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; - -class DelayField extends StatelessWidget { - const DelayField({ - Key? key, - required this.textEditingController, - }) : super(key: key); - - final TextEditingController textEditingController; - - @override - Widget build(BuildContext context) { - return TextField( - controller: textEditingController, - keyboardType: TextInputType.number, - decoration: const InputDecoration( - border: UnderlineInputBorder(), - icon: Icon(Icons.lock_clock_outlined), - labelText: "Delay service in ms", - ), - ); - } -} diff --git a/examples/app_async_flutter/lib/ui/atoms/input_field.dart b/examples/app_async_flutter/lib/ui/atoms/input_field.dart new file mode 100644 index 0000000..93f649b --- /dev/null +++ b/examples/app_async_flutter/lib/ui/atoms/input_field.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class InputField extends StatelessWidget { + const InputField( + {Key? key, + required this.textEditingController, + this.keyboardType = TextInputType.number, + required this.labelText, + this.icon}) + : super(key: key); + + final TextEditingController textEditingController; + final TextInputType? keyboardType; + final String labelText; + final IconData? icon; + + @override + Widget build(BuildContext context) { + return TextField( + controller: textEditingController, + keyboardType: keyboardType, + decoration: InputDecoration( + border: const UnderlineInputBorder(), + icon: Icon(icon), + labelText: labelText, + ), + ); + } +} diff --git a/examples/app_async_flutter/lib/ui/helpers/home_helper.dart b/examples/app_async_flutter/lib/ui/helpers/home_helper.dart index dc9bf19..8d555fe 100644 --- a/examples/app_async_flutter/lib/ui/helpers/home_helper.dart +++ b/examples/app_async_flutter/lib/ui/helpers/home_helper.dart @@ -2,26 +2,25 @@ import 'package:app_async_flutter/async_client_service.dart'; import 'package:flutter/material.dart'; class HomeHelper { - late final AsyncClientService asyncClientService; - HomeHelper(BuildContext context) { - asyncClientService = AsyncClientService.of(context)!; - } + final AsyncClientService asyncClientService; + HomeHelper(BuildContext context, this.asyncClientService); void callAsyncBackend(textEditingController) { int start = DateTime.now().millisecondsSinceEpoch; asyncClientService.asyncClientGateway .callBusinessUseCase( asyncClientService.prefs.getString("channelRef") ?? "", + asyncClientService.prefs.getString("userRef") ?? "", int.tryParse(textEditingController.text) ?? 100) .then((value) => asyncClientService.responsesNotifier.addResponse( "Get empty response after ${DateTime.now().millisecondsSinceEpoch - start} ms")); } - void disconnect() { - asyncClientService.closeSession(); + Future disconnect() async { + await asyncClientService.closeSession(); } - void connect() { - asyncClientService.initAsyncClient(); + Future connect() async { + await asyncClientService.initAsyncClient(); } } diff --git a/examples/app_async_flutter/lib/ui/pages/config_page.dart b/examples/app_async_flutter/lib/ui/pages/config_page.dart new file mode 100644 index 0000000..eed9399 --- /dev/null +++ b/examples/app_async_flutter/lib/ui/pages/config_page.dart @@ -0,0 +1,102 @@ +import 'package:app_async_flutter/ui/atoms/button.dart'; +import 'package:flutter/material.dart'; + +import '../../application/app_config.dart'; +import '../../async_client_service.dart'; +import '../../infrastructure/notifier/log_notifier.dart'; +import '../atoms/input_field.dart'; + +class ConfigPage extends StatefulWidget { + const ConfigPage({super.key}); + + @override + State createState() => _ConfigPageState(); +} + +class _ConfigPageState extends State { + TextEditingController heartbeatController = TextEditingController(); + TextEditingController maxRetriesController = TextEditingController(); + TextEditingController apiBusinessController = TextEditingController(); + TextEditingController socketController = TextEditingController(); + late AsyncClientService asyncClientService; + + @override + void initState() { + super.initState(); + heartbeatController.text = + AppConfig.of(context).heartbeatInterval.toString(); + maxRetriesController.text = AppConfig.of(context).maxRetries.toString(); + apiBusinessController.text = AppConfig.of(context).socketUrl; + socketController.text = AppConfig.of(context).businessUrl; + } + + @override + Widget build(BuildContext context) { + asyncClientService = AsyncClientService.of(context)!; + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + InputField( + textEditingController: heartbeatController, + labelText: "Heartbeat delay in ms", + icon: Icons.lock_clock_outlined), + const SizedBox(height: 20), + InputField( + textEditingController: maxRetriesController, + labelText: "max retries to connect", + icon: Icons.plus_one), + const SizedBox(height: 20), + InputField( + textEditingController: apiBusinessController, + labelText: "Socket url", + keyboardType: TextInputType.url, + icon: Icons.connect_without_contact_sharp), + const SizedBox(height: 20), + InputField( + textEditingController: socketController, + labelText: "api Business url", + keyboardType: TextInputType.url, + icon: Icons.api), + const SizedBox(height: 20), + const Text('Show all logs'), + Switch( + value: AppConfig.of(context).logNotifier.level == LogLevel.all, + onChanged: (value) { + AppConfig.of(context) + .logNotifier + .setLevel(value ? LogLevel.all : LogLevel.info); + setState(() {}); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('logs level saved'), + ), + ); + }, + ), + const Text( + 'If you disable this option, will set the log level to info'), + const SizedBox(height: 40), + Button( + onTap: () { + AppConfig.of(context).updateConfig( + heartbeatInterval: int.parse(heartbeatController.text), + maxRetries: int.parse(maxRetriesController.text), + socketUrl: apiBusinessController.text, + businessUrl: socketController.text); + asyncClientService.saveConfig(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Configuration saved, remember that the current connection does not take the changes, disconnect and reconnect.'), + ), + ); + }, + text: 'Save') + ], + ), + ); + } +} diff --git a/examples/app_async_flutter/lib/ui/pages/home_page.dart b/examples/app_async_flutter/lib/ui/pages/home_page.dart index 68a16cb..23b6f79 100644 --- a/examples/app_async_flutter/lib/ui/pages/home_page.dart +++ b/examples/app_async_flutter/lib/ui/pages/home_page.dart @@ -1,11 +1,9 @@ -import 'package:app_async_flutter/async_client_service.dart'; -import 'package:app_async_flutter/ui/atoms/button.dart'; -import 'package:app_async_flutter/ui/atoms/delay_field.dart'; -import 'package:app_async_flutter/ui/helpers/home_helper.dart'; -import 'package:app_async_flutter/ui/pages/log_viewer.dart'; +import 'package:app_async_flutter/ui/pages/config_page.dart'; +import 'package:app_async_flutter/ui/pages/request_page.dart'; import 'package:flutter/material.dart'; -import '../../application/app_config.dart'; +import '../../async_client_service.dart'; +import 'log_viewer_page.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); @@ -16,83 +14,52 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - late AsyncClientService asyncClientService; - late HomeHelper homeHelper; - TextEditingController textEditingController = TextEditingController(); + int _currentIndex = 0; + final List _children = [ + const RequestPage(), + const LogViewer(), + const ConfigPage(), + ]; - @override - void initState() { - super.initState(); - asyncClientService = AsyncClientService.of(context)!; - homeHelper = HomeHelper(context); - textEditingController.text = "250"; - - asyncClientService.initAsyncClient(); + void onTabTapped(int index) { + setState(() { + _currentIndex = index; + }); } @override - void dispose() { - asyncClientService.closeSession(); - super.dispose(); + void initState() { + super.initState(); + AsyncClientService.of(context)!.initAsyncClient(); } @override Widget build(BuildContext context) { + const items = [ + BottomNavigationBarItem( + icon: Icon(Icons.remove_from_queue_sharp), + label: 'Requests', + ), + BottomNavigationBarItem( + icon: Icon(Icons.list), + label: 'Logs', + ), + BottomNavigationBarItem( + icon: Icon(Icons.settings), + label: 'Config', + ), + ]; + return Scaffold( appBar: AppBar( title: Text(widget.title), ), - body: Padding( - padding: const EdgeInsets.all(40.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - DelayField(textEditingController: textEditingController), - const SizedBox(height: 10), - Row( - children: [ - Button( - text: "Generate Request", - onTap: () => - homeHelper.callAsyncBackend(textEditingController)), - const SizedBox(width: 10), - Button(text: "Re-Connect", onTap: () => homeHelper.connect()), - const SizedBox(width: 10), - Button( - text: "Disconnect", onTap: () => homeHelper.disconnect()), - ], - ), - const SizedBox(height: 10), - const Text("Response"), - const SizedBox(height: 10), - Button( - text: "Clean Logs", - onTap: () => asyncClientService.responsesNotifier.clean()), - const SizedBox(height: 10), - Expanded( - child: AnimatedBuilder( - animation: asyncClientService.responsesNotifier, - builder: (context, _) { - var data = asyncClientService.responsesNotifier.responses; - return ListView.builder( - itemCount: data.length, - itemBuilder: (context, index) => ListTile( - title: Text(data[index]), - textColor: data[index].contains("empty") - ? Colors.black45 - : Colors.black, - )); - }), - ), - const SizedBox(width: 10), - const Text("Captured Console Logs"), - Button( - text: "Clean Logs", - onTap: () => AppConfig.of(context).logNotifier.clean()), - const SizedBox(height: 10), - const Expanded(child: LogViewer()), - ], - ), + body: _children[_currentIndex], + bottomNavigationBar: BottomNavigationBar( + items: items, + selectedItemColor: Colors.amber[800], + onTap: onTabTapped, + currentIndex: _currentIndex, ), ); } diff --git a/examples/app_async_flutter/lib/ui/pages/log_viewer.dart b/examples/app_async_flutter/lib/ui/pages/log_viewer.dart deleted file mode 100644 index f32cb25..0000000 --- a/examples/app_async_flutter/lib/ui/pages/log_viewer.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../application/app_config.dart'; -import '../../infraestructure/notifier/log_notifier.dart'; - -class LogViewer extends StatefulWidget { - const LogViewer({super.key}); - - @override - State createState() => _LogViewerState(); -} - -class _LogViewerState extends State { - LogNotifier? logNotifier; - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - logNotifier?.dispose(); - } - - @override - Widget build(BuildContext context) { - WidgetsBinding.instance.addPostFrameCallback((_) { - logNotifier?.addListener(() { - setState(() {}); - }); - }); - logNotifier = AppConfig.of(context).logNotifier; - - return ListView.separated( - itemCount: logNotifier?.logs.length ?? 0, - separatorBuilder: (context, index) => - const SizedBox(height: 16), // Espacio entre elementos - itemBuilder: (context, index) { - return SelectableText(logNotifier?.logs[index] ?? ''); - }, - ); - } -} diff --git a/examples/app_async_flutter/lib/ui/pages/log_viewer_page.dart b/examples/app_async_flutter/lib/ui/pages/log_viewer_page.dart new file mode 100644 index 0000000..8f7266f --- /dev/null +++ b/examples/app_async_flutter/lib/ui/pages/log_viewer_page.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; + +import '../../application/app_config.dart'; +import '../../infrastructure/notifier/log_notifier.dart'; +import '../atoms/button.dart'; + +class LogViewer extends StatefulWidget { + const LogViewer({super.key}); + + @override + State createState() => _LogViewerState(); +} + +class _LogViewerState extends State { + LogNotifier? logNotifier; + @override + void initState() { + super.initState(); + logNotifier = AppConfig.of(context).logNotifier; + + WidgetsBinding.instance.addPostFrameCallback((_) { + logNotifier?.addListener(() { + if (mounted) { + setState(() {}); + } + }); + }); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + logNotifier?.removeListener(() { + if (mounted) { + setState(() {}); + } + }); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const SizedBox(height: 10), + Button( + text: "Clean Logs", + onTap: () => AppConfig.of(context).logNotifier.clean()), + const SizedBox(height: 10), + Expanded( + child: ListView.separated( + itemCount: logNotifier?.logs.length ?? 0, + separatorBuilder: (context, index) => + const SizedBox(height: 16), // Espacio entre elementos + itemBuilder: (context, index) { + return SelectableText(logNotifier?.logs[index] ?? ''); + }, + ), + ), + ], + ), + ); + } +} diff --git a/examples/app_async_flutter/lib/ui/pages/request_page.dart b/examples/app_async_flutter/lib/ui/pages/request_page.dart new file mode 100644 index 0000000..d3de38a --- /dev/null +++ b/examples/app_async_flutter/lib/ui/pages/request_page.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +import '../../async_client_service.dart'; +import '../atoms/button.dart'; +import '../atoms/input_field.dart'; +import '../helpers/home_helper.dart'; + +class RequestPage extends StatefulWidget { + const RequestPage({super.key}); + + @override + State createState() => _RequestPageState(); +} + +class _RequestPageState extends State { + late HomeHelper homeHelper; + late AsyncClientService asyncClientService; + TextEditingController textEditingController = TextEditingController(); + @override + void initState() { + super.initState(); + textEditingController.text = "250"; + } + + @override + Widget build(BuildContext context) { + asyncClientService = AsyncClientService.of(context)!; + homeHelper = HomeHelper(context, asyncClientService); + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InputField( + textEditingController: textEditingController, + labelText: "Delay in ms", + icon: Icons.lock_clock_outlined, + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Button( + text: "Request", + onTap: () => + homeHelper.callAsyncBackend(textEditingController)), + const SizedBox(width: 10), + Button(text: "Re-Connect", onTap: () => homeHelper.connect()), + const SizedBox(width: 10), + Button(text: "Disconnect", onTap: () => homeHelper.disconnect()), + ], + ), + const SizedBox(height: 10), + const Text("Response"), + const SizedBox(height: 10), + Button( + text: "Clean Logs", + onTap: () => asyncClientService.responsesNotifier.clean()), + const SizedBox(height: 10), + Expanded( + child: AnimatedBuilder( + animation: asyncClientService.responsesNotifier, + builder: (context, _) { + var data = asyncClientService.responsesNotifier.responses; + return ListView.builder( + itemCount: data.length, + itemBuilder: (context, index) => ListTile( + title: Text(data[index]), + textColor: data[index].contains("empty") + ? Colors.black45 + : Colors.black, + )); + }), + ), + const SizedBox(width: 10), + ], + ), + ); + } +} diff --git a/examples/app_async_flutter/pubspec.lock b/examples/app_async_flutter/pubspec.lock index e59a55f..7c3ba66 100644 --- a/examples/app_async_flutter/pubspec.lock +++ b/examples/app_async_flutter/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.2.0" args: dependency: transitive description: @@ -111,7 +111,7 @@ packages: path: "../../clients/client-dart" relative: true source: path - version: "2.0.2" + version: "2.0.3" characters: dependency: transitive description: @@ -180,10 +180,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.6" fake_async: dependency: transitive description: @@ -597,7 +597,7 @@ packages: source: hosted version: "1.3.2" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" diff --git a/examples/app_async_flutter/pubspec.yaml b/examples/app_async_flutter/pubspec.yaml index 1c68934..e6f923e 100644 --- a/examples/app_async_flutter/pubspec.yaml +++ b/examples/app_async_flutter/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: http: ^0.13.4 flutter_dotenv: ^5.0.2 logging: ^1.2.0 + uuid: ^3.0.7 dev_dependencies: flutter_test: