Artículos de la serie Flutter - Flutter Advanced 2

En esta sección, le presentaré en detalle temas avanzados de Flutter, incluida la navegación y el enrutamiento, la administración del estado, el procesamiento asincrónico, las solicitudes HTTP y Rest API, y la persistencia de datos. Repasemos cada uno de estos temas uno por uno.

1. Navegación y rutas

En Flutter, la navegación y el enrutamiento son conceptos clave para crear aplicaciones de varias páginas. La navegación es el proceso de cambiar de una página (o ruta) a otra. Cada página corresponde a un widget. En Flutter, el cambio de página lo gestiona Navigator.

1.1 Navegación básica

En Flutter, usa MaterialApp para administrar la pila de navegación. Cuando se crea una nueva MaterialApp, automáticamente crea una pila de enrutamiento y coloca un Navegador encima de la pila.

Para navegar a una nueva página, se puede utilizar el método Navigator.push():

Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));

Para volver a la página anterior, puede utilizar el método Navigator.pop():

Navigator.pop(context);

1.2 Nombrar rutas

Flutter también admite enrutamiento con nombres, lo que le permite usar nombres más legibles para la navegación en su aplicación. Para usar rutas con nombre, primero defina la tabla de rutas en MaterialApp:

MaterialApp(
  routes: {
    '/': (context) => HomePage(),
    '/second': (context) => SecondPage(),
  },
)

Luego puede utilizar rutas con nombre para la navegación:

Navigator.pushNamed(context, '/second');

1.3 Enrutamiento con parámetros

A veces es necesario pasar parámetros a páginas nuevas. En Flutter, puedes usar ModalRoute.of() para obtener los parámetros en la ruta:

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context).settings.arguments as Map<String, dynamic>;
    // 使用参数
    return Scaffold(...);
  }
}

Para pasar parámetros, puede pasar parámetros al navegar:

Navigator.pushNamed(context, '/second', arguments: {'name': 'John', 'age': 30});

1.4 Animación de transición de enrutamiento

Flutter proporciona una gran cantidad de efectos de animación de transición de enrutamiento, como degradado, zoom, panorámica, etc. Puede configurar PageTransitionsBuilder en MaterialPageRoute para personalizar la animación de transición:

MaterialApp(
  routes: {
    '/': (context) => HomePage(),
    '/second': (context) => SecondPage(),
  },
  theme: ThemeData(
    pageTransitionsTheme: PageTransitionsTheme(
      builders: {
        TargetPlatform.android: CupertinoPageTransitionsBuilder(), // 使用iOS样式的转场动画
      },
    ),
  ),
)

Esta es solo una introducción básica a la navegación y el enrutamiento. Flutter proporciona más funciones de navegación y enrutamiento, como animación de héroe, intercepción de enrutamiento, etc. Puede obtener más información sobre navegación y enrutamiento leyendo la documentación oficial y el código de muestra.

2. Gestión del Estado

En Flutter, la gestión del estado es un aspecto importante del manejo de datos compartidos y cambios de estado entre diferentes páginas de una aplicación. Hay una variedad de soluciones de gestión estatal en Flutter, entre las cuales Provider, Riverpod y Bloc son las más populares.

2.1. Proveedor

El proveedor es una biblioteca de administración de estado liviana y fácil de usar. Le permite compartir datos en el árbol de widgets y obtener datos a través de Consumer o Provider.of.

Primero, cree un ChangeNotifierProvider en el widget raíz de la aplicación y coloque el modelo de datos que se compartirá en él:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: MyApp(),
    ),
  );
}

Luego, en el widget que necesita usar datos, use Consumer para suscribirse a los cambios de datos:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CounterModel>();
    return Text('Count: ${counter.count}');
  }
}

Cuando los datos en CounterModel cambien, MyWidget se actualizará automáticamente.

2.2. vaina del río

Riverpod es una nueva biblioteca de gestión estatal, que es una versión mejorada de Provider. Riverpod ofrece un mejor rendimiento y una API más limpia.

Para usar Riverpod, primero cree un Proveedor:

final counterProvider = Provider<int>((ref) => 0);

Luego, use ProviderListener para suscribirse a los cambios de datos:

class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final counter = watch(counterProvider);
    return Text('Count: $counter');
  }
}

2.3. Bloque político

Bloc es otra biblioteca de gestión de estado de uso común que utiliza flujo de datos unidireccional para gestionar el estado. Los bloques separan el estado de las operaciones, lo que hace que el código sea más fácil de mantener y probar.

Primero, crea un bloque:

enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.increment:
        yield state + 1;
        break;
      case CounterEvent.decrement:
        yield state - 1;
        break;
    }
  }
}

Luego, en el widget que necesita usar Bloc, use BlocBuilder para suscribirse a los cambios de estado:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, int>(
      builder: (context, state) {
        return Text('Count: $state');
      },
    );
  }
}

Esta es solo una introducción básica a la administración estatal. Provider, Riverpod y Bloc brindan más funciones y uso avanzado. Se necesita tiempo y práctica para aprender en profundidad la gestión estatal, y puede dominar más habilidades y mejores prácticas leyendo documentos oficiales y códigos de muestra.

3. Procesamiento asincrónico

En Flutter, el procesamiento asincrónico es muy común, como obtener datos de la red, leer archivos locales, etc. Flutter proporciona Future y Stream para manejar operaciones asincrónicas.

3.1. Futuro

El futuro representa una operación asincrónica que puede completarse o fallar. Para ejecutar una tarea asincrónica, utilice las palabras clave async y await:

Future<String> fetchData() async {
  // 模拟网络请求
  await Future.delayed(Duration(seconds: 2));
  return 'Data from server';
}

void main() {
  fetchData().then((data) {
    print(data);
  }).catchError((error) {
    print('Error: $error');
  });
}

3.2. Arroyo

Stream representa una secuencia de eventos asincrónicos. A diferencia de Future, Stream puede producir múltiples valores en lugar de un único resultado.

Para crear un Stream, se puede utilizar un StreamController:

Stream<int> countStream() {
  final controller = StreamController<int>();
  Timer.periodic(Duration(seconds: 1), (timer) {
    controller.add(timer.tick);
  });
  return controller.stream;
}

void main() {
  countStream().listen((count) {
    print('Count: $count');
  });
}

Esta es solo una introducción básica al procesamiento asincrónico. Flutter también proporciona más herramientas y funciones asincrónicas, como async* y await for, que pueden manejar operaciones asincrónicas de manera más conveniente. El estudio en profundidad del procesamiento asincrónico requiere práctica e intentos continuos. Espero que puedas dominar estas tecnologías en proyectos reales.

4. Solicitud HTTP y API Rest

Interactuar con servidores es un requisito común en las aplicaciones modernas. Flutter proporciona una variedad de formas de realizar solicitudes HTTP y manejar Rest API.

4.1 Usando el paquete http

El paquete http de Flutter es una biblioteca de solicitudes HTTP fácil de usar que le permite enviar solicitudes HTTP y manejar respuestas.

Primero, agregue las dependencias del paquete http al archivo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3

Luego, puede utilizar el paquete http para enviar solicitudes HTTP:

import 'package:http/http.dart' as http;

Future<void> fetchData() async {
  final url = Uri.parse('https://api.example.com/data');
  final response = await http.get(url);

  if (response.statusCode == 200) {
    print('Response: ${response.body}');
  } else {
    print('Error: ${response.statusCode}');
  }
}

4.2 Usando el paquete Dio

dio es otra biblioteca de solicitudes HTTP popular que proporciona funciones más completas y una API fácil de usar.

Primero, agregue la dependencia del paquete dio al archivo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  dio: ^4.0.0

Luego, puedes usar el paquete dio para enviar solicitudes HTTP:

import 'package:dio/dio.dart';

Future<void> fetchData() async {
  final dio = Dio();
  final url = 'https://api.example.com/data';
  final response = await dio.get(url);

  if (response.statusCode == 200) {
    print('Response: ${response.data}');
  } else {
    print('Error: ${response.statusCode}');
  }
}

4.3 Procesamiento de datos JSON

Normalmente los datos devueltos por el servidor están en formato JSON. En Flutter, puedes usar el paquete dart:convert para analizar y serializar datos JSON.

import 'dart:convert';

void main() {
  final jsonString = '{"name": "John", "age": 30}';
  final jsonData = jsonDecode(jsonString);

  print('Name: ${jsonData['name']}');
  print('Age: ${jsonData['age']}');
}

Esta es sólo una introducción básica a las solicitudes HTTP y al manejo de datos JSON. En un proyecto real, es posible que también necesites manejar errores, usar clases de modelo para serializar datos, etc. Espero que puedas aprender más sobre la solicitud HTTP y la API Rest estudiando la documentación oficial y el código de muestra.

5. Persistencia de datos

La persistencia de los datos en la aplicación es esencial y Flutter proporciona una variedad de formas de lograr el almacenamiento local de datos.

5.1 Usando el paquete Shared_Preferences

shared_preferences es un repositorio local fácil de usar que puede almacenar datos de pares clave-valor.

Primero, agregue la dependencia del paquete share_preferences al archivo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.0.9

Luego, el paqueteshared_preferences se puede utilizar para leer y escribir datos:

import 'package:shared_preferences/shared_preferences.dart';

void main() async {
  final prefs = await SharedPreferences.getInstance();

  // 保存数据
  await prefs.setString('username', 'John');

  // 读取数据
  final username = prefs.getString('username');
  print('Username: $username');
}

5.2 Usando el paquete sqflite

sqflite es un paquete de base de datos SQLite que proporciona funciones de base de datos más potentes y es adecuado para escenarios que necesitan almacenar datos complejos.

Primero, agregue la dependencia del paquete sqflite al archivo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0+4

Luego, el paquete sqflite se puede utilizar para crear y administrar bases de datos:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

void main() async {
  final databasePath = await getDatabasesPath();
  final database = await openDatabase(
    join(databasePath, 'app_database.db'),
    version: 1,
    onCreate: (db, version) {
      db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
    },
  );

  // 插入数据
  await database.insert('users', {'name': 'John'});

  // 查询数据
  final users = await database.query('users');
  for (var user in users) {
    print('User: ${user['name']}');
  }
}

Esta es sólo una introducción básica a la persistencia de datos. En proyectos reales, es posible que también deba ocuparse de migraciones de bases de datos, utilizar marcos ORM, etc. Espero que pueda aprender más sobre la persistencia de datos estudiando documentos oficiales y códigos de muestra.

6. Demostración completa

El siguiente es un código de muestra completo que incluye navegación y enrutamiento, administración de estado, procesamiento asincrónico, solicitudes HTTP y API Rest, y persistencia de datos. Este ejemplo utilizará un proveedor para administrar el estado y recuperar datos a través de solicitudes HTTP y guardarlos en una base de datos SQLite.

Primero, agregue dependencias al archivo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  provider: ^6.0.1
  http: ^0.13.3
  sqflite: ^2.0.0+4

Luego, cree cuatro archivos Dart para crear el ejemplo:

main.dart: define MyApp como el widget raíz y crea MaterialApp.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:http/http.dart' as http;

import 'home_page.dart';
import 'user.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => UserProvider()),
        ],
        child: HomePage(),
      ),
    );
  }
}

home_page.dart: define HomePage como la página para mostrar información del usuario.

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

import 'user.dart';
import 'second_page.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final userProvider = Provider.of<UserProvider>(context);
    final users = userProvider.users;

    return Scaffold(
      appBar: AppBar(
        title: Text('User List'),
      ),
      body: ListView.builder(
        itemCount: users.length,
        itemBuilder: (context, index) {
          final user = users[index];
          return ListTile(
            title: Text(user.name),
            subtitle: Text('Email: ${user.email}'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => SecondPage(user: user),
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await userProvider.fetchUsersFromApi();
        },
        child: Icon(Icons.refresh),
      ),
    );
  }
}

second_page.dart: define SecondPage como una página que muestra información sobre un único usuario.

import 'package:flutter/material.dart';

import 'user.dart';

class SecondPage extends StatelessWidget {
  final User user;

  SecondPage({required this.user});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User Detail'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(user.name, style: TextStyle(fontSize: 24)),
            SizedBox(height: 10),
            Text('Email: ${user.email}', style: TextStyle(fontSize: 18)),
            SizedBox(height: 10),
            Text('Phone: ${user.phone}', style: TextStyle(fontSize: 18)),
            SizedBox(height: 10),
            Text('Website: ${user.website}', style: TextStyle(fontSize: 18)),
          ],
        ),
      ),
    );
  }
}

user.dart: define la clase de usuario y el proveedor de usuario para la gestión del estado y la persistencia de los datos.

import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:http/http.dart' as http;
import 'dart:convert'; // 添加此导入

class User {
  final String name;
  final String email;
  final String phone;
  final String website;

  User(
      {required this.name,
      required this.email,
      this.phone = '',
      this.website = ''});
}

class UserProvider extends ChangeNotifier {
  List<User> _users = [];

  List<User> get users => _users;

  Future<void> fetchUsersFromApi() async {
    final response =
        await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
    if (response.statusCode == 200) {
      final List<dynamic> data = json.decode(response.body); // 使用json.decode方法
      _users = data
          .map((item) => User(
              name: item['name'],
              email: item['email'],
              phone: item['phone'],
              website: item['website']))
          .toList();
      notifyListeners();
      saveUsersToDatabase();
    }
  }

  Future<void> saveUsersToDatabase() async {
    final dbPath = await getDatabasesPath();
    final database = await openDatabase(join(dbPath, 'user_database.db'),
        version: 1, onCreate: (db, version) {
      db.execute(
        'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, phone TEXT, website TEXT)',
      );
    });

    await database.delete('users');
    for (var user in _users) {
      await database.insert('users', {
        'name': user.name,
        'email': user.email,
        'phone': user.phone,
        'website': user.website
      });
    }
  }

  Future<void> loadUsersFromDatabase() async {
    final dbPath = await getDatabasesPath();
    final database =
        await openDatabase(join(dbPath, 'user_database.db'), version: 1);

    final List<Map<String, dynamic>> maps = await database.query('users');
    _users = maps
        .map((map) => User(
            name: map['name'],
            email: map['email'],
            phone: map['phone'],
            website: map['website']))
        .toList();
    notifyListeners();
  }
}

Este ejemplo obtendrá datos del usuario a través de una solicitud HTTP y utilizará el proveedor para administrar los datos del usuario. Los datos del usuario se guardarán en una base de datos SQLite y se cargarán desde la base de datos cuando se inicie la aplicación.

Resumir

En este artículo, presentamos en detalle temas avanzados de Flutter, que incluyen navegación y enrutamiento, administración de estado, procesamiento asincrónico, solicitudes HTTP y API Rest, y persistencia de datos. Estos temas son muy importantes en aplicaciones prácticas y le ayudan a crear aplicaciones Flutter más complejas y potentes.

Aprender temas avanzados requiere práctica y exploración continuas. Espero que pueda profundizar su comprensión y dominar estas tecnologías leyendo documentos oficiales y códigos de muestra. ¡Te deseo progreso y éxito en tu aprendizaje y desarrollo con Flutter! Si tienes alguna pregunta, no dudes en preguntarme, haré todo lo posible para ayudarte.

Supongo que te gusta

Origin blog.csdn.net/xudepeng0813/article/details/132009629
Recomendado
Clasificación