Necessitarem tres cursors, cada un amb la seva etiqueta; per tant, el més raonable és crear un element genèric que es pugui personalitzar; així els tres cursors tindran un aspecte semblant.
Per mantenir el programa ben organitzat, els elements estaran en carpetes. Dins de la carpeta lib crearem una carpeta anomenada components, on guardarem els diferents blocs. Aquest element el posarem en el fitxer cursor.dart que crearem a la carpeta components. Atès que hi haurà paràmetres variables, crearem un giny amb estat que anomenarem Cursor.
cursor.dart
import 'package:flutter/material.dart';
class Cursor extends StatefulWidget {
const Cursor({super.key});
@override
State<Cursor> createState() => _CursorState();
}
class _CursorState extends State<Cursor> {
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
L'usuari entrarà el contingut de cada component de color en un valor de vuit bits; per tant, el cursor anirà entre 0 i 255. Atès que, per defecte, els cursors van entre 0 i 1, cal indicar quins són els límits; aprofitem per establir que hi ha 255 posicions en el recorregut, de manera que el valor sempre sigui enter. El valor de la posició en la que estarà el cursor correspondrà a la variable valor (que ha de ser real) i el corresponent a la posició en la qual el desplaci l'usuari anirà a la variable valorNou. La posició inicial del cursor serà 127; o sigui, al centre. També definim què cal fer quan canviï; en aquest cas, guardar el nou valor a la variable valor. Això ho hem de posar dins d'un setState, per tal que s'actualitzi cada cop que hi hagi un canvi.
...
class _CursorState extends State<Cursor> {
double valor = 127;
@override
Widget build(BuildContext context) {
return Slider(
value: valor,
min: 0,
max: 255,
divisions: 255,
onChanged: (valorNou) {
valor = valorNou;
},
);
}
}
A sobre del cursor volem que hi figuri el color que estem ajustant i el valor que hem seleccionat. Per aconseguir-ho, farem una columna, dins la qual posarem una filera amb els dos elements de text i el cursor, que quedarà sota de la filera. De moment, el nom del color és un text fix.
...
class _CursorState extends State<Cursor> {
double valor = 127;
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Text("Color:"),
Text(valor.toStringAsFixed(0))
],
),
Slider(
value: valor,
min: 0,
max: 255,
divisions: 255,
onChanged: (valorNou) {
setState(() {
valor = valorNou;
});
},
),
],
);
}
}
Voldríem que el conjunt ocupés l'amplada de la pantalla i que els textos no s'enganxessin a les vores de la pantalla. També seria convenient que quedés un text a cada costat. Per ajustar els marges, posarem la columna dins d'un farciment. Separarem els textos amb un espaiador, que forçarà que es situïn als extrems de la filera.
...
class _CursorState extends State<Cursor> {
double valor = 127;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Row(
children: [
Text("Color:"),
Spacer(),
Text(valor.toStringAsFixed(0))
],
),
Slider(
value: valor,
min: 0,
max: 255,
divisions: 255,
onChanged: (valorNou) {
setState(() {
valor = valorNou;
});
},
),
],
),
);
}
}
Els textos queden més propers als marges que el cursor, a causa del propi disseny intern d'aquest. Però el cursor té un paràmetre que permet modificar els marges.
...
Slider(
value: valor,
min: 0,
max: 255,
divisions: 255,
padding: const EdgeInsets.only(left: 4, right: 4, top: 12, bottom: 12),
onChanged: (valorNou) {
setState(() {
valor = valorNou;
});
},
),
...
Si fem que se'ns mostri l'aplicació en l'emulador, ens adonarem que caldria, si més no, definir alguns colors, ja que és difícil llegir els textos. Crearem un estil per als textos que acompanyen el cursor. Per estructurar bé l'aplicació, definirem tots els estils en un fitxer separat; així, si en algun moment els volem canviar, només els cal modificar en un únic lloc. Abans de res, dins de la carpeta core, crearem el fitxer estils.dart, on definirem els estils que intervindran en la nostra aplicació. Posarem una mida una mica grossa (per exemple, 18), en negreta i el color corresponent al text. De moment, el fitxer quedarà així:
estils.dart
import 'package:flutter/material.dart'; import 'package:selector_color/core/colors.dart';
class Estils {
static const TextStyle estilTextCursors = TextStyle(
color: ColorsApp.text,
fontSize: 18,
fontWeight: FontWeight.bold,
);
}
I aplicarem l'estil als dos textos:
...
Row(
children: [
Text("Color:", style: Estils.estilTextCursors),
Spacer(),
Text(valor.toStringAsFixed(0), style: Estils.estilTextCursors),
],
),
...
Per tal de poder personalitzar els cursors i que siguin els tres diferents, serà necessari passar algun paràmetre. Atès que necessitarem el valor a la pantalla principal, també caldrà passar aquest valor i la funció que el modifica. En el giny del cursor definirem dues variables (per al títol i el valor) i una funció, que haurà de retornar un valor real. El títol i el valor es faran servir en els ginys de text i la funció en la definició de què cal fer si es mou el cursor, el paràmetre de la funció serà el valor corresponent a la nova posició del cursor.
class Cursor extends StatefulWidget {
final String titol;
final double valor;
final Function(double) siCanvia;
const Cursor({super.key, required this.titol, required this.valor, required this.siCanvia});
@override
State<Cursor> createState() => _CursorState();
}
class _CursorState extends State<Cursor> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Row(
children: [
Text(widget.titol, style: Estils.estilTextCursors),
Spacer(),
Text(widget.valor.toStringAsFixed(0), style: Estils.estilTextCursors),
],
),
Slider(
value: widget.valor,
min: 0,
max: 255,
divisions: 255,
padding: const EdgeInsets.only(left: 4, right: 4, top: 12, bottom: 12),
onChanged: (valorNou) {
setState(() {
widget.siCanvia(valorNou);
});
},
),
],
),
);
}
}
A la pantalla principal caldrà definir una variable per al valor de cada cursor. De moment només n'hi haurà (valors) una perquè només n'hem posat un. També definim la funció agafant el paràmetre (que hem anomenat nouValor) i assignant-lo a la variable.
...
class _PantPralState extends State<PantPral> {
double valors = 127;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Tria un color"),
backgroundColor: ColorsApp.primari,
foregroundColor: ColorsApp.text,
),
backgroundColor: ColorsApp.fons,
body: Column(
children: [
Cursor(
titol: "Vermell:",
valor: valors,
siCanvia: (nouValor) {
setState(() {
valors = nouValor;
});
},
),
],
),
);
}
}
Ara que ja tenim el giny del cursor força avançat, en podem ja posar tres a la pantalla principal. En lloc de crear una variable per a cada color, és preferible definir una llista.
...
class _PantPralState extends State<PantPral> {
List<double> valors = [127, 127, 127];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Tria un color"),
backgroundColor: ColorsApp.primari,
foregroundColor: ColorsApp.text,
),
backgroundColor: ColorsApp.fons,
body: Column(
children: [
Cursor(
titol: "Vermell:", valor: valors[0],
siCanvia: (nouValor) {
setState(() {valors[0] = nouValor;});
},
),
Cursor(
titol: "Verd:", valor: valors[1],
siCanvia: (nouValor) {
setState(() {valors[1] = nouValor;});
},
),
Cursor(
titol: "Blau:", valor: valors[2],
siCanvia: (nouValor) {
setState(() {valors[2] = nouValor;});
},
),
],
),
);
}
}
Els cursors es podrien quedar així o bé canviar-los els colors. El que farem serà posar un color diferent a cada un, que remarqui quina és la component de color que es modifica. Per fer-ho d'una manera més genèrica, definirem els tres colors en una llista. A la part dreta dels cursors hi posarem el color primari, que ja estava definit. Al fitxer colors.dart cal afegir-hi:
... static ListcolCursors = [Color(0xFFFF0000), Color(0xFF00FF00), Color(0xFF0000FF)]; ...
cursor.dart
import 'package:flutter/material.dart'; import 'package:selector_color/core/colors.dart'; import 'package:selector_color/core/estils.dart';
class Cursor extends StatefulWidget {
final String titol;
final double valor;
final Color colCurs;
final Function(double) siCanvia;
const Cursor({
super.key,
required this.titol,
required this.valor,
required this.colCurs,
required this.siCanvia,
});
@override
State<Cursor> createState() => _CursorState();
}
class _CursorState extends State<Cursor> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Row(
children: [
Text(widget.titol, style: Estils.estilTextCursors),
Spacer(),
Text(
widget.valor.toStringAsFixed(0),
style: Estils.estilTextCursors,
),
],
),
Slider(
value: widget.valor, min: 0, max: 255, divisions: 255,
activeColor: widget.colCurs,
inactiveColor: ColorsApp.primari,
padding: const EdgeInsets.only(left: 4, right: 4, top: 12, bottom: 12),
onChanged: (valorNou) {
setState(() {
widget.siCanvia(valorNou);
});
},
),
],
),
);
}
}
A la pantalla principal només cal afegir el color com a paràmetre.
pant_principal.dart
import 'package:flutter/material.dart'; import 'package:selector_color/components/cursor.dart'; import 'package:selector_color/core/colors.dart';
...
body: Column(
children: [
Cursor(
titol: "Vermell:", valor: valors[0], colCurs: ColorsApp.colCursors[0],
siCanvia: (nouValor) {
setState(() {valors[0] = nouValor;});
},
),
Cursor(
titol: "Verd:", valor: valors[1], colCurs: ColorsApp.colCursors[1],
siCanvia: (nouValor) {
setState(() {valors[1] = nouValor;});
},
),
Cursor(
titol: "Blau:", valor: valors[2], colCurs: ColorsApp.colCursors[2],
siCanvia: (nouValor) {
setState(() {valors[2] = nouValor;});
},
),
],
),
...

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