Configurar tu proyecto
Instrucciones sobre cómo crear una nueva app de Flutter.
Previsualiza la aplicación lectora de Wikipedia que vas a construir y configura el proyecto inicial con los paquetes requeridos.
Qué lograrás
Pasos
1
Introducción
Introducción
En las próximas lecciones, aprenderás a trabajar con datos en una aplicación Flutter. Construirás una app que obtiene y muestra resúmenes de artículos de la API de Wikipedia.
Estas lecciones exploran:
- Realizar solicitudes HTTP en Flutter.
- Administrar el State de la aplicación con
ChangeNotifier. - Usar el patrón de arquitectura MVVM.
- Crear interfaces de usuario responsivas que se actualizan automáticamente cuando los datos cambian.
Este tutorial asume que has completado los tutoriales Primeros pasos con Dart y el de Introducción a la UI de Flutter, y por lo tanto no explica conceptos como HTTP, JSON o los aspectos básicos de los widgets.
2
Crear un nuevo proyecto Flutter
Crear un nuevo proyecto Flutter
Crea un nuevo proyecto Flutter utilizando la CLI de Flutter. En tu terminal preferida, ejecuta el siguiente comando para crear una aplicación Flutter mínima:
flutter create wikipedia_reader --empty
3
Añadir las dependencias requeridas
Añadir las dependencias requeridas
Tu aplicación necesita el paquete http para realizar solicitudes HTTP.
Añádelo a tu proyecto:
cd wikipedia_reader && flutter pub add http
4
Examinar el código inicial
Examinar el código inicial
Primero, crea un nuevo archivo lib/summary.dart para definir el modelo de datos
para los resúmenes de artículos de Wikipedia. Este archivo no tiene lógica especial y es
simplemente una colección de clases que representan los datos devueltos por la
API de Wikipedia. Es suficiente con copiar el código a continuación en el archivo y luego ignorarlo.
Si no te sientes cómodo con las clases básicas de Dart, deberías leer el tutorial Primeros pasos con Dart
primero.
/// Representation of the JSON data returned by the Wikipedia API.
class Summary {
/// Returns a new [Summary] instance.
Summary({
required this.titles,
required this.pageId,
required this.extract,
required this.extractHtml,
required this.lang,
required this.dir,
required this.url,
this.description,
this.thumbnail,
this.originalImage,
});
/// The title information of this article.
final TitlesSet titles;
/// The page ID of this article.
final int pageId;
/// The first few sentences of the article in plain text.
final String extract;
/// The first few sentences of the article in HTML format.
final String extractHtml;
/// The language code of the article's content, such as "en" for English.
final String lang;
/// The text directionality of the article's content, such as "ltr" or "rtl".
final String dir;
/// The URL of the page.
final String url;
/// A description of the article, if available.
final String? description;
/// A thumbnail-sized version of the article's primary image, if available.
final ImageFile? thumbnail;
/// The original full-sized article's primary image, if available.
final ImageFile? originalImage;
/// Whether this article has an image.
bool get hasImage => originalImage != null && thumbnail != null;
/// Returns a new [Summary] instance and imports its values from a JSON map
static Summary fromJson(Map<String, Object?> json) {
return switch (json) {
{
'titles': final Map<String, Object?> titles,
'pageid': final int pageId,
'extract': final String extract,
'extract_html': final String extractHtml,
'thumbnail': final Map<String, Object?> thumbnail,
'originalimage': final Map<String, Object?> originalImage,
'lang': final String lang,
'dir': final String dir,
'description': final String description,
'content_urls': {
'desktop': {'page': final String url},
'mobile': {'page': String _},
},
} =>
Summary(
titles: TitlesSet.fromJson(titles),
pageId: pageId,
extract: extract,
extractHtml: extractHtml,
thumbnail: ImageFile.fromJson(thumbnail),
originalImage: ImageFile.fromJson(originalImage),
lang: lang,
dir: dir,
description: description,
url: url,
),
{
'titles': final Map<String, Object?> titles,
'pageid': final int pageId,
'extract': final String extract,
'extract_html': final String extractHtml,
'lang': final String lang,
'dir': final String dir,
'description': final String description,
'content_urls': {
'desktop': {'page': final String url},
'mobile': {'page': String _},
},
} =>
Summary(
titles: TitlesSet.fromJson(titles),
pageId: pageId,
extract: extract,
extractHtml: extractHtml,
lang: lang,
dir: dir,
description: description,
url: url,
),
{
'titles': final Map<String, Object?> titles,
'pageid': final int pageId,
'extract': final String extract,
'extract_html': final String extractHtml,
'lang': final String lang,
'dir': final String dir,
'content_urls': {
'desktop': {'page': final String url},
'mobile': {'page': String _},
},
} =>
Summary(
titles: TitlesSet.fromJson(titles),
pageId: pageId,
extract: extract,
extractHtml: extractHtml,
lang: lang,
dir: dir,
url: url,
),
_ => throw FormatException('Could not deserialize Summary, json=$json'),
};
}
@override
String toString() =>
'Summary['
'titles=$titles, '
'pageId=$pageId, '
'extract=$extract, '
'extractHtml=$extractHtml, '
'thumbnail=${thumbnail ?? 'null'}, '
'originalImage=${originalImage ?? 'null'}, '
'lang=$lang, '
'dir=$dir, '
'description=$description'
']';
}
// Image path and size, but doesn't contain any Wikipedia descriptions.
class ImageFile {
/// Returns a new [ImageFile] instance.
ImageFile({required this.source, required this.width, required this.height});
/// The URI of the original image.
final String source;
/// The width of the original image.
final int width;
/// The height of the original image.
final int height;
/// The file extension of the image, or 'err' if one can't be determined.
String get extension {
final extension = getFileExtension(source);
// By default, return a non-viable image extension.
return extension ?? 'err';
}
/// Returns a JSON map representation of this [ImageFile].
Map<String, Object?> toJson() {
return <String, Object?>{
'source': source,
'width': width,
'height': height,
};
}
/// Returns a new [ImageFile] instance with its values populated from [json].
static ImageFile fromJson(Map<String, Object?> json) {
if (json case {
'source': final String source,
'height': final int height,
'width': final int width,
}) {
return ImageFile(source: source, width: width, height: height);
}
throw FormatException('Could not deserialize OriginalImage, json=$json');
}
@override
String toString() =>
'OriginalImage[source_=$source, width=$width, height=$height]';
}
class TitlesSet {
/// Returns a new [TitlesSet] instance.
TitlesSet({
required this.canonical,
required this.normalized,
required this.display,
});
/// The non-prefixed DB key for the article.
///
/// Might contain changes such as underscores instead of spaces.
/// Best suited for making request URIs, but still requires percent-encoding.
final String canonical;
/// The [normalized title](https://www.mediawiki.org/wiki/API:Query#Example_2:_Title_normalization)
/// of the article.
final String normalized;
/// The title as it should be displayed to the user.
final String display;
/// Returns a new [TitlesSet] instance with its values populated from [json].
static TitlesSet fromJson(Map<String, Object?> json) {
if (json case {
'canonical': final String canonical,
'normalized': final String normalized,
'display': final String display,
}) {
return TitlesSet(
canonical: canonical,
normalized: normalized,
display: display,
);
}
throw FormatException('Could not deserialize TitleSet, json=$json');
}
@override
String toString() =>
'TitlesSet['
'canonical=$canonical, '
'normalized=$normalized, '
'display=$display'
']';
}
String? getFileExtension(String file) {
final segments = file.split('.');
if (segments.isNotEmpty) return segments.last;
return null;
}
const acceptableImageFormats = ['png', 'jpg', 'jpeg'];
Luego, abre lib/main.dart y reemplaza el código existente con
esta estructura básica, que añade las importaciones requeridas que utiliza la aplicación:
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'summary.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Wikipedia Flutter')),
body: const Center(child: Text('Loading...')),
),
);
}
}
Este código proporciona una estructura de aplicación básica con una barra de título y contenido de marcador de posición. Las importaciones en la parte superior incluyen todo lo necesario para solicitudes HTTP, análisis de JSON y el modelo de datos de Wikipedia.
5
Ejecutar tu aplicación
Ejecutar tu aplicación
Prueba que todo funcione ejecutando tu aplicación:
flutter run -d chrome
Deberías ver una aplicación sencilla con "Wikipedia Flutter" en la barra de la aplicación (app bar) y "Loading..." en el centro de la pantalla.
6
Revisión
Revisión
Qué lograste
Aquí tienes un resumen de lo que construiste y aprendiste en esta lección.Previsualicé la aplicación lectora de Wikipedia
Estás comenzando una nueva sección de tutorial enfocada en trabajar con datos. Aprenderás sobre solicitudes HTTP, State Management con
ChangeNotifier y el patrón de arquitectura MVVM.
Añadí el paquete http y creé un modelo de datos
Utilizaste flutter pub add para instalar el paquete http para realizar solicitudes HTTP y creaste la
clase Summary para los datos de Wikipedia. Los paquetes te permiten aprovechar el código existente construido por la comunidad en lugar de construir todo desde cero.
Configuré la estructura del proyecto inicial
Tu aplicación tiene la estructura básica con todas las importaciones necesarias para solicitudes HTTP, análisis de JSON y datos de Wikipedia. ¡Estás listo para comenzar a obtener datos reales de la API de Wikipedia!
7
Ponte a prueba
Ponte a prueba
Quiz de configuración del proyecto
1 / 2--empty flag do when running flutter create?Crea un proyecto que solo se puede ejecutar en la web.
No exactamente.
El flag no restringe las plataformas; solo afecta a la plantilla de inicio.
Crea un proyecto Flutter mínimo con menos código boilerplate (de plantilla).
¡Así es!
El flag
--emptygenera una plantilla de inicio mínima sin la aplicación de contador por defecto.Crea un proyecto sin dependencias.
No exactamente.
El proyecto aún incluye las dependencias principales de Flutter.
Crea un proyecto sin ningún archivo en absoluto.
No exactamente.
El proyecto aún tiene los archivos esenciales; solo utiliza una plantilla mínima.
flutter package install [package_name]No exactamente.
No existe el comando
flutter package; utilizaflutter pub add.flutter pub add [package_name]¡Así es!
Ejecutar
flutter pub addañade el paquete a pubspec.yaml y lo descarga.dart get [package_name]No exactamente.
El comando para añadir paquetes es
flutter pub addo editar pubspec.yaml.flutter install [package_name]No exactamente.
El comando correcto utiliza
pub add, noinstall.
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-05. Ver código fuente oreportar un problema.