Tabla de Contenidos
Introducción
En este artículo aprenderás a consumir la API REST de WordPress desde Flutter, una solución ideal para quienes ya tienen un blog o sitio web en WordPress y quieren mostrar su contenido en una app móvil. Vamos a usar una arquitectura moderna basada en Riverpod
y GoRouter
, con soporte para paginación, vista de detalles y organización modular.
Requisitos previos
- Tener un sitio en WordPress con la API REST habilitada (por defecto viene activa).
- Conocimientos básicos de Flutter y Riverpod.
- Tener configurado un proyecto Flutter funcional.
Paso 1: Crear la estructura base del proyecto
Organizaremos el proyecto en capas. Dentro del directorio principal de tu app Flutter, crea las siguientes carpetas:
lib/
├── core/
├── models/
├── services/
├── features/
│ └── posts/
├── providers/
├── routing/
└── main.dart
Cada carpeta tendrá una función específica:
core/
: constantes, configuración global, helpers.models/
: definición de modelos comoPost
.services/
: lógica de conexión a la API REST.features/posts/
: UI y lógica específica de los posts.providers/
: control de estado con Riverpod.routing/
: configuración de rutas con GoRouter.
Paso 2: Crear el modelo Post
Este modelo representa la estructura de un artículo del blog. Extraemos datos como el título, el contenido, la imagen destacada y la fecha desde el JSON que devuelve WordPress. Usamos Post.fromJson()
para transformar los datos en una instancia del modelo.
Archivo: lib/models/post.dart
class Post {
final int id;
final String title;
final String excerpt;
final String content;
final String featuredImage;
final DateTime date;
Post({
required this.id,
required this.title,
required this.excerpt,
required this.content,
required this.featuredImage,
required this.date,
});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title']['rendered'],
excerpt: json['excerpt']['rendered'],
content: json['content']['rendered'],
featuredImage: json['_embedded']?['wp:featuredmedia']?[0]?['source_url'] ?? '',
date: DateTime.parse(json['date']),
);
}
}
✅ Asegúrate de incluir
?_embed
en las solicitudes para acceder a la imagen destacada.
Paso 3: Crear el servicio para consumir la API
Aquí encapsulamos la lógica para conectarnos con la API REST de WordPress. Usamos la función fetchPosts()
para obtener los artículos en formato JSON y los convertimos en una lista de objetos Post
. También manejamos errores si la respuesta no es exitosa.
Archivo: lib/services/api_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/post.dart';
class ApiService {
static const String baseUrl = 'https://jaracoder.com/wp-json/wp/v2';
Future<List<Post>> fetchPosts({int page = 1}) async {
final response = await http.get(Uri.parse('$baseUrl/posts?_embed&page=$page'));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
return data.map((json) => Post.fromJson(json)).toList();
} else {
throw Exception('Error al cargar los posts');
}
}
}
Paso 4: Crear el provider con Riverpod
Este provider es responsable de cargar los artículos desde la API y exponerlos a la UI de forma reactiva. Usamos FutureProvider.family
para permitir pasar la página como parámetro (útil para la paginación).
Archivo: lib/providers/posts_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/post.dart';
import '../services/api_service.dart';
final postsProvider = FutureProvider.family<List<Post>, int>((ref, page) async {
final api = ApiService();
return api.fetchPosts(page: page);
});
Paso 5: Crear una pantalla para mostrar los artículos
En esta pantalla mostramos los artículos usando un ListView
. Riverpod se encarga de escuchar el provider y renderizar automáticamente el contenido cuando esté disponible. También mostramos indicadores de carga y errores si ocurren.
Archivo: lib/features/posts/posts_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../providers/posts_provider.dart';
class PostsScreen extends ConsumerWidget {
const PostsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final postsAsync = ref.watch(postsProvider(1));
return Scaffold(
appBar: AppBar(title: const Text('Últimos artículos')),
body: postsAsync.when(
data: (posts) => ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => ListTile(
title: Text(posts[index].title),
subtitle: Text(posts[index].excerpt),
),
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('Error: $e')),
),
);
}
}
Paso 6: Agregar la ruta con GoRouter
Usamos GoRouter para definir una ruta principal que cargue la pantalla de posts. Esto nos permite navegar de forma declarativa y organizada. Más adelante podremos añadir más rutas para categorías, favoritos o detalles de cada artículo.
Archivo: lib/routing/app_router.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../features/posts/posts_screen.dart';
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const PostsScreen(),
),
],
);
Archivo: lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'routing/app_router.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: router,
title: 'Jaracoder Blog',
theme: ThemeData.light(),
);
}
}