Introducción al Concepto
Este proyecto demuestra la compartición de código entre aplicaciones Flutter y servidores Dart, habilitando una base de código unificada para múltiples plataformas. Enfocado en OpenHarmony, ilustra cómo reutilizar modelos de datos y lógica de negocio para mejorar la eficiencia y consistencia.
Características Principales
- Reutilización de modelos de datos Dart en clientes y servidores
- Clases de datos inmutables generadas con Freezed
- Serialización automática de JSON mediante json_serializable
- Ejemplo de comunicación HTTP entre cliente Flutter y servidor Dart
- Compatibilidad con aplicaciones OpenHarmony
Mecanismo Técnico
La compartición se logra a través de un paquete independiente que contiene modelos compartidos. Se emplean anotaciones de Freezed para garantizar inmutabilidad, json_serializable para el menejo de JSON, y el paquete http para la comuniacción REST. Variables de entorno permiten configurar endpoints del servidor.
Configuración en OpenHarmony
Para integrar en un proyecto OpenHarmony, incluya las dependencias necesarias en el archivo pubspec.yaml:
dependencies:
flutter:
sdk: flutter
http:
git:
url: "https://gitcode.com/openharmony-tpc/flutter_packages.git"
path: "packages/http/http"
modelos_compartidos:
git:
url: "https://gitcode.com/openharmony-tpc/flutter_samples.git"
path: "code_sharing/modelos_compartidos"
Ejecute flutter pub get para obtener las dependencias. Adicionalmente, en el archivo module.json5 del módulo OpenHarmony, añada el permiso de red:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
Ejemplos de Uso
Modelos de Datos Compartidos
import 'package:modelos_compartidos/modelos_compartidos.dart';
final contador = ValorContador(42);
print('Valor actual: ${contador.cantidad}');
final incremento = Incremento(en: 5);
print('Incremento en: ${incremento.en}');
final jsonContador = contador.toJson();
print('JSON del contador: $jsonContador');
final contadorDesdeJson = ValorContador.fromJson(jsonContador);
print('Contador desde JSON: ${contadorDesdeJson.cantidad}');
Comunicación Cliente-Servidor
import 'dart:convert';
import 'dart:io' show Platform;
import 'package:http/http.dart' as cliente_http;
import 'package:modelos_compartidos/modelos_compartidos.dart';
import 'package:flutter/material.dart';
final puerto = Platform.environment['PUERTO'] ?? '8080';
final host = Platform.environment['URL_SERVIDOR'] ?? '127.0.0.1';
final esquema = Platform.environment['ESQUEMA_SERVIDOR'] ?? 'http';
final urlServidor = Uri.parse('$esquema://$host:$puerto/');
Future<int> obtenerValorActual() async {
final respuesta = await cliente_http.get(urlServidor);
final contador = ValorContador.fromJson(json.decode(respuesta.body));
return contador.cantidad;
}
Future<int> ejecutarIncremento(int cantidad) async {
final incremento = Incremento(en: cantidad);
final respuesta = await cliente_http.post(
urlServidor,
body: json.encode(incremento.toJson()),
headers: {'Content-Type': 'application/json'},
);
final contador = ValorContador.fromJson(json.decode(respuesta.body));
return contador.cantidad;
}
class AplicacionContador extends StatelessWidget {
const AplicacionContador({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo de Code Sharing',
theme: ThemeData(primarySwatch: Colors.blue),
home: const PaginaContador(),
);
}
}
class PaginaContador extends StatefulWidget {
const PaginaContador({super.key});
@override
State<PaginaContador> createState() => _EstadoPaginaContador();
}
class _EstadoPaginaContador extends State<PaginaContador> {
int _valorContador = 0;
bool _cargando = false;
@override
void initState() {
super.initState();
_cargarValor();
}
void _cargarValor() async {
setState(() => _cargando = true);
try {
final valor = await obtenerValorActual();
setState(() => _valorContador = valor);
} finally {
setState(() => _cargando = false);
}
}
void _incrementarValor() async {
setState(() => _cargando = true);
try {
final valor = await ejecutarIncremento(1);
setState(() => _valorContador = valor);
} finally {
setState(() => _cargando = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Demo de Code Sharing')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Valor actual:',
style: TextStyle(fontSize: 20),
),
Text(
'$_valorContador',
style: Theme.of(context).textTheme.headlineMedium,
),
if (_cargando) ...[
const SizedBox(height: 20),
const CircularProgressIndicator(),
],
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _cargando ? null : _incrementarValor,
tooltip: 'Incrementar',
child: const Icon(Icons.add),
),
);
}
}
Implemantación del Servidor
import 'dart:convert';
import 'dart:io';
import 'package:modelos_compartidos/modelos_compartidos.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_router/shelf_router.dart';
int valorGlobal = 0;
final enrutador = Router()
..post('/', _manejadorIncremento)
..get('/', _manejadorObtenerValor);
Future<Response> _manejadorIncremento(Request solicitud) async {
final inc = Incremento.fromJson(json.decode(await solicitud.readAsString()));
valorGlobal += inc.en;
return Response.ok(json.encode(ValorContador(valorGlobal).toJson()));
}
Response _manejadorObtenerValor(Request solicitud) =>
Response.ok(json.encode(ValorContador(valorGlobal).toJson()));
void main(List<String> argumentos) async {
final direccionIp = InternetAddress.anyIPv4;
final pipeline = Pipeline().addMiddleware(logRequests()).addHandler(enrutador);
final puerto = int.parse(Platform.environment['PUERTO'] ?? '8080');
final servidor = await serve(pipeline, direccionIp, puerto);
print('Servidor escuchando en el puerto ${servidor.port}');
}