Si consultem les dades que obtenim, podem veure que hi ha més camps, a banda del nom i la professió. Anem a fer que, quan es piqui sobre una persona del llistat, s'obri una segona pantalla que mostri més informació.
Començarem per crear una pantalla, que podem anomenar PantDetalls i guardar-la com a pant_detalls.dart dins de la carpeta screens. Pot ser que més endavant aquesta pantalla tingui accions i, per tant, serà millor crear-la com a giny amb estat. Caldrà passar-li les dades de la persona a mostrar, que serà un objecte del tipus DadesDonaLlista. Per fer una primera prova, només hi posarem un text que mostri el nom de la persona.
pant_detalls.dart
import 'package:dones_destacades/data/dades_dona_llista.dart'; import 'package:flutter/material.dart';
class PantDetalls extends StatefulWidget {
final DadesDonaLlista persona;
const PantDetalls({super.key, required this.persona});
@override
State<PantDetalls> createState() => _PantDetallsState();
}
class _PantDetallsState extends State<PantDetalls> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text(widget.persona.nom),
);
}
}
Ara ens cal cridar aquest giny des de la pantalla principal. Allà tenim un mètode anomenat mostraElement, que és el que mostra cada un dels elements de la llista. Per a cada un es crea una columna; per tant, el més pràctic és fer que es canviï de pantalla cada cop que es pica sobre la columna. Així, es passarà a la segona pantalla tant si es pica sobre el nom com sobre la professió. Per tant, posarem la columna dins d'un detector d'accions.
...
Padding mostraElement(DadesDonaLlista donaDeLaLlista) {
var unescape = HtmlUnescape();
return Padding(
padding: const EdgeInsets.all(5.0),
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => PantDetalls(persona: donaDeLaLlista),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(unescape.convert(donaDeLaLlista.nom),
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
Text(unescape.convert(donaDeLaLlista.professio),
style: TextStyle(fontSize: 13)),
]
)
)
);
}
}
Si ho provem, veurem que funciona però mostra el nom amb els caràcters especials codificats; aviat ho arreglarem. També ens queda el text mal situat, però això es pot millorar afegint la barra de l'aplicació, on podem mostrar el nom de la persona.
class _PantDetallsState extends State<PantDetalls> {
@override
Widget build(BuildContext context) {
var unescape = HtmlUnescape();
return Scaffold(
appBar: AppBar(
title: Text(unescape.convert(widget.persona.nom)),
),
body: Text(unescape.convert(widget.persona.nom)),
);
}
}
Anem a mirar les dades que rebem per a una persona concreta:
{
"Id": "1826",
"Ambit": "PS",
"Nom": "Àngels Ferrer Sensat",
"Nom casada": "",
"Altre nom": "",
"Professio": "Científica catalana",
"Fet destacat 1": "Va ser innovadora en metodologies d'ensenyament"
},
De moment, gestionem les dues que estan marcades. Podem modificar la classe DadesDonaLlista per obtenir les altres que ens interessin. Al mètode DadesDonaLlista hi afegirem la resta de camps:
class DadesDonaLlista {
final String id;
final String ambit;
final String nom;
final String casada;
final String alies;
final String professio;
final String destacat;
DadesDonaLlista({
required this.id,
required this.ambit,
required this.nom,
required this.casada,
required this.alies,
required this.professio,
required this.destacat,
});
factory DadesDonaLlista.fromJson(Map<String, dynamic> dadesJson) {
return DadesDonaLlista(
id: dadesJson["Id"] ?? "",
ambit: dadesJson["Ambit"] ?? "",
nom: dadesJson["Nom"] ?? "",
casada: dadesJson["Nom casada"] ?? "",
alies: dadesJson["Altre nom"] ?? "",
professio: dadesJson["Professio"] ?? "",
destacat: dadesJson["Fet destacat 1"] ?? "",
);
}
}
Alguns dels camps poden estar buits; quan això passa, rebrem un nul. Per evitar problemes, hem posat un valor alternatiu que correspon a un text buit; per evitar problemes, ho hem fet amb tots els camps.
Ara anem a mostrar els resultats a la segona pantalla. Hem posat una columna, a la qual posem blocs de text amb una mica de format; per exemple, hem deixat una petita separació entre els noms i la professió. En el cas del nom de casada i l'àlies hem posat un text al davant per tal que s'entengui què són aquests dos camps. Ens hem saltat dos camps (id i ambit) perquè no aporten informació útil. Si ho provem, observarem que no queda gaire bé que apareguin aquestes línies si no hi ha noms per mostrar.
...
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(unescape.convert(widget.persona.nom), style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
Text("Nom de casada: ${unescape.convert(widget.persona.casada)}", style: TextStyle(fontSize: 13)),
Text("Àlies: ${unescape.convert(widget.persona.alies)}", style: TextStyle(fontSize: 13)),
SizedBox(height: 5),
Text(unescape.convert(widget.persona.professio), style: TextStyle(fontSize: 14)),
Text(unescape.convert(widget.persona.destacat), style: TextStyle(fontSize: 13)),
],
),
...
Ho podem arreglar fàcilment amb una condició; però cal tenir en compte que, atès que estem fent una llista de ginys, només podem fer servir la sentència if en una sola línia (sense claus).
...
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(unescape.convert(widget.persona.nom), style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
if(widget.persona.casada.isNotEmpty)
Text("Nom de casada: ${unescape.convert(widget.persona.casada)}", style: TextStyle(fontSize: 13)),
if(widget.persona.alies.isNotEmpty)
Text("Àlies: ${unescape.convert(widget.persona.alies)}", style: TextStyle(fontSize: 13)),
SizedBox(height: 5),
Text(unescape.convert(widget.persona.professio), style: TextStyle(fontSize: 14)),
Text(unescape.convert(widget.persona.destacat), style: TextStyle(fontSize: 13)),
],
),
...
Els diferents fitxers de l'aplicació, doncs, quedaran així:
main.dart
import 'package:dones_destacades/screens/pant_principal.dart'; import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: PantPrincipal()
);
}
}
pant_principal.dart
import 'package:dones_destacades/data/crida_llista_dones.dart'; import 'package:dones_destacades/data/dades_dona_llista.dart'; import 'package:dones_destacades/data/dades_rebudes_dones.dart'; import 'package:dones_destacades/screens/pant_detalls.dart'; import 'package:flutter/material.dart'; import 'package:html_unescape/html_unescape.dart'; // No ho posa automàticament
class PantPrincipal extends StatefulWidget {
const PantPrincipal({super.key});
@override
State<PantPrincipal> createState() => _PantPrincipalState();
}
class _PantPrincipalState extends State<PantPrincipal> {
Future<DadesRebudesDones?>? _dadesDones;
CridaLlistaDones cridaLlistaDones = CridaLlistaDones();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Dones destacades"),),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
setState(() {
_dadesDones = cridaLlistaDones.demanarLlistaDones();
});
},
child: Text("Dones catalanes"),
),
),
mostraLlista()
],
),
);
}
FutureBuilder<DadesRebudesDones?> mostraLlista() {
return FutureBuilder(future: _dadesDones, builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return CircularProgressIndicator();
} else if(snapshot.hasError){
return Text("Error: ${snapshot.error}");
} else if(snapshot.hasData){
var llistaDones = snapshot.data?.llistaDadesDonaLlista;
return Expanded(
child: ListView.builder(
itemCount: llistaDones?.length ?? 0,
itemBuilder: (context, index) {
if(llistaDones != null){
return mostraElement(llistaDones[index]);
} else {
return Text("Error a la llista");
}
},
),
);
} else {
return Text("No s'han trobat resultats");
}
});
}
Padding mostraElement(DadesDonaLlista donaDeLaLlista) {
var unescape = HtmlUnescape();
return Padding(
padding: const EdgeInsets.all(5.0),
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => PantDetalls(persona: donaDeLaLlista),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(unescape.convert(donaDeLaLlista.nom),
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
Text(unescape.convert(donaDeLaLlista.professio),
style: TextStyle(fontSize: 13)),
]
)
)
);
}
}
pant_detalls.dart
import 'package:dones_destacades/data/dades_dona_llista.dart'; import 'package:flutter/material.dart'; import 'package:html_unescape/html_unescape.dart';
class PantDetalls extends StatefulWidget {
final DadesDonaLlista persona;
const PantDetalls({super.key, required this.persona});
@override
State<PantDetalls> createState() => _PantDetallsState();
}
class _PantDetallsState extends State<PantDetalls> {
@override
Widget build(BuildContext context) {
var unescape = HtmlUnescape();
return Scaffold(
appBar: AppBar(
title: Text(unescape.convert(widget.persona.nom)),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(unescape.convert(widget.persona.nom),
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
if(widget.persona.casada.isNotEmpty)
Text("Nom de casada: ${unescape.convert(widget.persona.casada)}",
style: TextStyle(fontSize: 13)),
if(widget.persona.alies.isNotEmpty)
Text("Àlies: ${unescape.convert(widget.persona.alies)}",
style: TextStyle(fontSize: 13)),
SizedBox(height: 5),
Text(unescape.convert(widget.persona.professio), style: TextStyle(fontSize: 14)),
Text(unescape.convert(widget.persona.destacat), style: TextStyle(fontSize: 13)),
],
),
);
}
}
crida_llista_dones.dart
import 'dart:convert'; import 'package:dones_destacades/data/dades_rebudes_dones.dart'; import 'package:http/http.dart' as http; // No el troba automàticament, s'ha d'entrar a mà
class CridaLlistaDones{
Future<DadesRebudesDones?> demanarLlistaDones() async{
final llistaDonesRebuda = await
http.get(Uri.parse("https://recursos.citcea.upc.edu/dones/service.php?tipus=cat"));
if(llistaDonesRebuda.statusCode == 200){
var jsonDecodificat = jsonDecode(llistaDonesRebuda.body);
DadesRebudesDones dadesRebudesDones = DadesRebudesDones.fromJson(jsonDecodificat);
return dadesRebudesDones;
} else {
return null;
}
}
}
dades_rebudes_dones.dart
import 'package:dones_destacades/data/dades_dona_llista.dart';
class DadesRebudesDones {
final String descrip;
final List<DadesDonaLlista> llistaDadesDonaLlista;
DadesRebudesDones({required this.descrip, required this.llistaDadesDonaLlista});
factory DadesRebudesDones.fromJson(Map<String, dynamic> jsonRebut){
var dadesJsonRebut = jsonRebut["dades"] as List;
List<DadesDonaLlista> llistaDadesDona =
dadesJsonRebut.map((dona) => DadesDonaLlista.fromJson(dona)).toList();
return DadesRebudesDones(
descrip: jsonRebut["info"]["descrip"],
llistaDadesDonaLlista: llistaDadesDona
);
}
}
dades_dona_llista.dart
class DadesDonaLlista {
final String id;
final String ambit;
final String nom;
final String casada;
final String alies;
final String professio;
final String destacat;
DadesDonaLlista({
required this.id,
required this.ambit,
required this.nom,
required this.casada,
required this.alies,
required this.professio,
required this.destacat,
});
factory DadesDonaLlista.fromJson(Map<String, dynamic> dadesJson) {
return DadesDonaLlista(
id: dadesJson["Id"] ?? "",
ambit: dadesJson["Ambit"] ?? "",
nom: dadesJson["Nom"] ?? "",
casada: dadesJson["Nom casada"] ?? "",
alies: dadesJson["Altre nom"] ?? "",
professio: dadesJson["Professio"] ?? "",
destacat: dadesJson["Fet destacat 1"] ?? "",
);
}
}

Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.