Ara modificarem el programa per introduir-hi uns canvis que el fàcil més útil i còmode de fer servir.
Començarem per mostrar quatre botons, que els posarem en una filera. Atès que l'espai és limitat, posem botons d'icona en lloc de botons de text. El primer botó servirà per esborrar el contingut del quadre de text, el segon per llegir la posició GPS, el tercer per afegir la posició GPS al missatge i el quart per enviar les dades.
El tercer botó té dues icones (GPS i afegir) que agruparem en una filera, atès que la icona del botó admet un giny. Aquest botó només estarà disponible quan ja tinguem coordenades i, per tant, l'acció no es farà si la variable posOk no és certa. Quan el botó no està disponible, es mostra en un color més clar.
El quart botó només envia dades si hi ha algun text al requadre, per evitar que Telegram retorni un error. Quan el botó no està operatiu es mostra en un color més clar.
...
Padding(
padding: const EdgeInsets.only(left: 30, right: 30),
child: Row(
children: [
IconButton(
color: Colors.black,
onPressed: () {
setState(() {_quadreText.clear();});
},
icon: Icon(Icons.clear),
),
Spacer(),
IconButton(
color: Colors.black,
onPressed: llegeixPosicio,
icon: Icon(Icons.gps_fixed),
),
Spacer(),
IconButton(
color: posOk ? Colors.black : const Color(0xFFC3C3C3),
onPressed: () {setState(() {
if(posOk){_afegirText();}
});},
icon: Row(
children: [
Icon(Icons.gps_fixed),
Icon(Icons.add),
],
),
),
Spacer(),
IconButton(
color: txtEnv.isNotEmpty ? Colors.black : const Color(0xFFC3C3C3),
onPressed: () {setState(() {
if(txtEnv.isNotEmpty){_rebut = cridaWeb.enviaDades(txtEnv);}
});},
icon: Icon(Icons.send),
),
],
),
),
...
La funció enviaDades retornava la resposta completa enviada per Telegram, això pot ser útil durant les proves però no interessa a l'usuari. Modifiquem la funció perquè retorni un text més adient.
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<String> enviaDades(missat) async{
String resp = "L'enviament ha fallat";
final resposta =
await http.get(Uri.parse("$urlTgm$urlBase$missat"));
String decoded = utf8.decode(resposta.bodyBytes);
if(decoded.contains('"ok":true')){
resp = "Enviament correcte";
}
return resp;
}
}
Farem que es puguin incorporar les coordenades de geolocalització en el missatge. Per això caldrà proveir d'un controlador al camp de text i crear una variable on guardarem les coordenades tal com volem que surtin al missatge. La funció _afegirText s'encarregarà d'afegir les coordenades al text existent i posar el cursor al final. També simplificarem la visualització, posant un únic bloc de text, que anirà variant el seu contingut. El missatge a Telegram s'envia amb una ordre GET, que té un límit de caràcters; per axò hem limitat la mida del quadre de text a 2000 caràcters, això fa que se'ns mostri un comptador de caràcters a sota del camp.
pant_principal.dart
import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.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> {
String _textMostrar = "";
String _textAfegir = "";
String txtEnv = "";
bool posOk = false;
Future<String>? _rebut;
CridaWeb cridaWeb = CridaWeb();
final TextEditingController _quadreText = TextEditingController();
@override
void initState() {
super.initState();
comprovaPermisos();
}
Future<void> comprovaPermisos() async {
LocationPermission permis = await Geolocator.checkPermission();
if (permis == LocationPermission.denied) {
setState(() {
_textMostrar = 'Permís desactivat. Demanant permís...';
});
permis = await Geolocator.requestPermission();
}
setState(() {
if (permis == LocationPermission.whileInUse || permis == LocationPermission.always) {
_textMostrar = 'Permís activat.';
} else {
_textMostrar = 'Permís denegat permanentment.';
}
});
}
Future<void> llegeixPosicio() async {
try {
final LocationSettings configPosicio = LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 100,
);
Position posicio = await Geolocator.getCurrentPosition(locationSettings: configPosicio);
setState(() {
_textMostrar = 'Coordenades: ${posicio.latitude}, ${posicio.longitude}';
_textAfegir = ' ${posicio.latitude}, ${posicio.longitude} ';
posOk = true;
});
} catch (e) {
setState(() {_textMostrar = 'Error: $e';});
}
}
void _afegirText() {
final textActual = _quadreText.text;
final int posicioCursor = _quadreText.selection.baseOffset; // On està el cursor
String nouText = "";
// Si no hi ha cursor (o està al principi), baseOffset retorna -1 o 0
if (posicioCursor < 0) {
// Afegim el text al final
_quadreText.text = textActual + _textAfegir;
txtEnv = textActual + _textAfegir;
} else {
// Inserim el text a la posició actual del cursor
nouText = textActual.substring(0, posicioCursor) +
_textAfegir + textActual.substring(posicioCursor);
_quadreText.text = nouText;
txtEnv = nouText;
}
_quadreText.selection = TextSelection.fromPosition(
TextPosition(offset: nouText.length),
);
}
@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,
maxLength: 2000,
decoration: InputDecoration(
hintText: "Escriu el que vols enviar",
border: OutlineInputBorder(),
),
controller: _quadreText,
onChanged: (txt){txtEnv = txt;},
),
),
SizedBox(height: 20),
Text(_textMostrar),
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("");
}
}),
Padding(
padding: const EdgeInsets.only(left: 30, right: 30),
child: Row(
children: [
IconButton(
color: Colors.black,
onPressed: () {
setState(() {_quadreText.clear();});
},
icon: Icon(Icons.clear),
),
Spacer(),
IconButton(
color: Colors.black,
onPressed: llegeixPosicio,
icon: Icon(Icons.gps_fixed),
),
Spacer(),
IconButton(
color: posOk ? Colors.black : const Color(0xFFC3C3C3),
onPressed: () {setState(() {
if(posOk){_afegirText();}
});},
icon: Row(
children: [
Icon(Icons.gps_fixed),
Icon(Icons.add),
],
),
),
Spacer(),
IconButton(
color: txtEnv.isNotEmpty ? Colors.black : const Color(0xFFC3C3C3),
onPressed: () {setState(() {
if(txtEnv.isNotEmpty){_rebut = cridaWeb.enviaDades(txtEnv);}
});},
icon: Icon(Icons.send),
),
],
),
),
],
),
);
}
}
Widget escriuResultats(String? dades){
if(dades != null){
return Text(dades.toString());
}
return Text("Alguna cosa ha fallat");
}
Nota: En aquest programa hi ha una funció que s'ha desenvolupat a partir d'una consulta a Google Gemini.

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