Començarem amb una versió senzilla de l'aplicació que ens permetrà enviar un missatge de text. En aquest exemple ja no explicarem tots els passos que ja s'han comentat en els exemples inicials, per donar èmfasi en les particularitats d'aquesta aplicació. Tampoc ens preocuparem gaire per l'aspecte de l'aplicació.
La nostra aplicació està en la carpeta telegram. El programa principal tindrà el contingut habitual i cridarà a la pantalla principal.
main.dart
import 'package:telegram/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()
);
}
}
La pantalla principal estarà en el fitxer pant_principal.dart, dins de la carpeta screens; que haurem creat a la carpeta lib. Inicialment, la pantalla principal tindrà un camp de text, on l'usuari podrà escriure, i un botó, que serà el que picarà per enviar el missatge. Per experiència dels exemples anteriors, aquests elements els hem posat dins d'un farciment. Els dos farciments estan dins d'una columna, perquè es mostrin l'un sota l'altre.
pant_principal.dart
import 'package:flutter/material.dart';
class PantPrincipal extends StatefulWidget {
const PantPrincipal({super.key});
@override
State<PantPrincipal> createState() => _PantPrincipalState();
}
class _PantPrincipalState extends State<PantPrincipal> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Enviar missatge"),),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
maxLines: 2,
decoration: InputDecoration(
hintText: "Escriu el que vols enviar",
border: OutlineInputBorder(),
),
onChanged: (txt){
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
setState(() {
});
},
child: Text("Envia text"),
),
),
],
),
);
}
}
Quan piquem aquest botó, s'ha de fer una crida web per enviar el text. La informació que rebrem serà la resposta de Telegram. De moment, la decodificarem i la mostrarem en la pantalla.
Ara ens falta la classe que s'encarrega de fer la crida web. Aquesta classe l'anomenarem CridaWeb i estarà en el fitxer crida_web.dart, que crearem a la carpeta data. Els codis per accedir a Telegram els posarem en un fitxer separat perquè quedin més amagats i, al mateix temps, siguin més fàcils de modificar. Per facilitar la comprensió, a continuació hi ha l'arbre de carpetes i fitxers:
lib
main.dart
data
claus_telegram.dart
crida_web.dart
screens
pant_principal.dart
El fitxer de les claus serà el següent:
claus_telegram.dart
class ClausTelegram{
static const token
= "^^34628844:AAFIpk-e7j3UZtYQYQaTduf4mPhnDqIcNXI";
static const usrId = "^^9199456";
}
A la classe CridaWeb, crearem el mètode enviaDades. Des que es faci la crida fins que arribi la resposta pot passar un temps; o, fins i tot, pot no arribar mai. Per tant, aquest mètode no el podrem definir com els que hem fet servir fins ara. Per un costat, el resultat del mètode serà un futur (Future), un tipus especial d'estructura de dades que s'omple de manera diferida. Per un altre costat, s'ha declarat com a asíncron (async) per tal que la resta de l'aplicació pugui continuar funcionant mentre s'espera l'arribada de les dades.
Ara és quan anem a fer la crida web; amb la funció http.get. El resultat de la lectura el guardarem a la variable resposta. Per tal que la funció http.get estigui disponible, hem de fer dues coses. Per un costat, cal anar al fitxer pubspec.yaml per dir-li que la volem fer servir. Cal posar-ho a dependencies, indicant quina és la versió que volem. És recomanable indicar la darrera versió; atès que quan comencem a escriure http ens ajuda a autocompletar, el normal és que ja ens doni la darrera versió. En el cas de la imatge, la darrera era la 1.5.0.

Un cop afegit, cal picar al botó per baixar el paquet (
).
Per un altre costat, cal importar el paquet http.dart; ja que no ho fa de manera automàtica.
Si es preveu que l'aplicació es pugui instal·lar en dispositius que tinguin versions antiques d'Android, cal donar permís d'accés a internet. Això ho farem anar al fitxer AndroidManifest.xml que es troba a la carpeta main de la carpeta src de la carpeta app de la carpeta android i afegir la línia del permís després de la de manifest i abans de la d'application:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
...
Atenció: Cal tenir en compte que l'absència d'aquest permís pot donar problemes a l'executar l'aplicació en el dispositiu, malgrat aquesta funcioni correctament en el simulador. Per assegurar que l'aplicació funcionarà en qualsevol dispositiu Android, convé posar aquesta línia sempre que s'hagn de fer connexions a internet.
Obtindrem les dades del servidor amb la comanda http.get, a la qual se li ha de passar l'adreça en format URI. Per convertir l'adreça de l'enllaç a aquest format, fem servir la funció Uri.parse. El resultat no es pot assignar directament a la variable resposta, perquè trigarà en arribat; per això afegim l'ordre await, per indicar que ha d'esperar el resultat. La resposta tampoc serà directament text, per això fem servir la funció utf8.decode per fer la conversió. L'adreça és força complexa, per això l'hem distribuït en dues variables.
crida_web.dart
import 'dart:convert'; import 'package:telegram/data/claus_telegram.dart'; import 'package:http/http.dart' as http;
class CridaWeb{
String urlTgm =
"https://api.telegram.org/bot${ClausTelegram.token}";
String urlBase =
"/sendMessage?chat_id=${ClausTelegram.usrId}&text=";
Future enviaDades(missat) async{
final resposta =
await http.get(Uri.parse("$urlTgm$urlBase$missat"));
String decoded = utf8.decode(resposta.bodyBytes);
return decoded;
}
}
Ens falta completar la pantalla principal. Comencem pel camp de text. Crearem una variable txtEnv on es guardarà el text que escriu l'usuari. El contingut de la variable canviarà cada cop que l'usuari modifiqui el contingut del camp.
...
Widget build(BuildContext context) {
String txtEnv = "";
return Scaffold(
appBar: AppBar(title: Text("Enviar missatge"),),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
maxLines: 2,
decoration: InputDecoration(
hintText: "Escriu el que vols enviar",
border: OutlineInputBorder(),
),
onChanged: (txt){
txtEnv = txt;
},
),
),
...
El botó cridarà a la funció enviaDades, el que retorni es posarà a la variable _rebut, que també haurem de crear.
...
class _PantPrincipalState extends State<PantPrincipal> {
Future<String>? _rebut;
CridaWeb cridaWeb = CridaWeb();
@override
Widget build(BuildContext context) {
...
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
setState(() {
_rebut = cridaWeb.enviaDades(txtEnv);
});
},
child: Text("Envia text"),
),
),
...
Finalment, com hem fet en exemples anteriors, tindrem un constructor de futurs que analitzarà el resultat de la comunicació i, si tot ha anat bé, cridarà la funció escriuResultats que ens mostrarà el que ha retornat Telegram.
pant_principal.dart
import 'package:flutter/material.dart'; import 'package:telegram/data/crida_web.dart';
class PantPrincipal extends StatefulWidget {
const PantPrincipal({super.key});
@override
State<PantPrincipal> createState() => _PantPrincipalState();
}
class _PantPrincipalState extends State<PantPrincipal> {
Future<String>? _rebut;
CridaWeb cridaWeb = CridaWeb();
@override
Widget build(BuildContext context) {
String txtEnv = "";
return Scaffold(
appBar: AppBar(title: Text("Enviar missatge"),),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
maxLines: 2,
decoration: InputDecoration(
hintText: "Escriu el que vols enviar",
border: OutlineInputBorder(),
),
onChanged: (txt){
txtEnv = txt;
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
setState(() {
_rebut = cridaWeb.enviaDades(txtEnv);
});
},
child: Text("Envia text"),
),
),
FutureBuilder(future: _rebut, builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return CircularProgressIndicator();
} else if(snapshot.hasError){
return Text("Error: ${snapshot.error}");
} else if(snapshot.hasData){
return escriuResultats(snapshot.data);
} else {
return Text("");
}
})
],
),
);
}
}
Widget escriuResultats(String? dades){
if(dades != null){
return Text(dades.toString());
}
return Text("Alguna cosa ha fallat");
}

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