Para poder mostrar un calendario en flutter existe, la función showDatePicker , la cual esta bastante bien, pero personalmente no me gusta y le hacia falta una característica que buscaba que es: fijar solo un mes de determinado año y el poder seleccionar un rango de días.
Vi unos paquetes muy buenos para implementar un calendario, como este flutter_calendar_carousel, si buscan un calendario bastante bonito y funcional, este podría ser el que quieren, pero a este ultimo le hacia falta seleccionar el rango de días.
Entonces se me ocurrió hacer uno parecido al calendar_caroucel, mas simple y con opción de seleccionar un el rango de días.
Siguiendo mi aprendizaje sobre flutter, como este de aquí, les explico lo que hice a continuación.
Desarrollo del calendario
Nota: Solo quieres usar el componente? entonces ve directo al paquete de flutter aquí.
El widget con fecha del mes de Junio 2020 se ve así:

Como se ve la organización de los días esta de este modo: Lunes, Martes, Miércoles, Jueves, Viernes, Sábado, Domingo.
Lo programe de este modo por que la función para obtener el día de la semana en dart empieza por el día lunes (ISO 8601) y no quise liarme con eso, ejemplo:
int year = DateTime(2020,6,1).year; int month = DateTime(2020,6,1).month; DateTime initDate = DateTime(2020,6,1); int totalDays = lastDayOfMonth(initDate).day;//30 int weekday = initDate.weekday;//1 dia lunes
Obtener los días del Mes
Para obtener el dato totalDays, me base en el siguiente algoritmo:

Basado en lo anterior se obtiene las siguientes conclusiones:
- La primer columna de la tabla empieza el día lunes y la ultima el viernes.
- El texto de los días deberá ir dentro de la tabla, igual que los números, entonces..
- la tabla sera de 7×7, con un total de 49 celdas.
Algoritmo
Todos los datos de la tabla estarán en un vector que luego se partira cada 7 posiciones, de esta manera:
L | M | M | . | . | . | . | 30 | 31 | n=42 |
Tenemos dos Widgets que podemos usar para posicionar elementos verticales Column() y horizontales Row()
El diseño entonces quedaría de la siguiente manera:

Así tendremos un diseño de una tabla en flutter, en la imagen hay 7 elementos dentro de un Row(), los primeros 7 pertenecen a las letras de los días de la semana y los siguientes serán los días y/o espacios en blanco en caso de que, o bien el día 1 empiece en un día que no sea lunes o el día 31 termine en un día que no sea domingo, y todo eso dentro de un elemento Column().
Llenar vector
Ya comente que los datos se guardaran en un vector de tamaño N=42, este vector se partirá en 6 secciones.
El vector que se declara es de este tipo:
List<Widget> _celds = List<Widget>();
Las primeras posiciones sera el texto de los días, ejemplo:
L | M | M | M | J | V | S | D |
Estas están hardcode dentro del código, cosa que pienso cambiar después ya que las letras están en Español y quisiera soportar el ingles.
Una vez agregadas las letras, nos queda llenar los días del mes, para ello hago una comprobación al inicio y otra al final.
Los días del mes son de tipo FlatButton, esto para que cada día, tenga un evento click y pueda saber que día estoy seleccionando.
Inicio, si el día 1 del mes establecido empieza en un día que no sea lunes, entonces el vector se llena, N posiciones con texto vació, hasta weekday-1.
Comprobación final, si el día ultimo del mes establecido es menor al día viernes, entonces se termina de llenar el vector hasta la posición 42, en un ciclo que empieza por la posición totalDays.
En medio de esas dos comprobaciones simplemente se llena el vector desde el día 1 hasta el día totalDays.
Una vez que este el vector este lleno, lo que hice fue ir partiendo el vector cada 7 posiciones y tomando 7 elementos, (aquí son 7 debido a que los vectores empiezan en 0 y en tamaño es es N-1 ).
Como el vector es de tipo List, en dart existe unas funciones para trabajar con vectores que utilize, para este caso son skip() y take()
Skip(), lo que hace es que omite N elementos dentro de una secuencia de elementos.
take(), toma N elementos dentro de la secuencia de elementos empezando por el primer elemento.
Al final para armar todos los elementos tipo Row(), que necesito meter en la Column(), hago lo que se muestra en la siguiente imagen:

Hay una lista ahi que tiene por nombre rows y no es mas que una lista de Row() elementos.
List<Row> rows = List<Row>();
Al final incluso en el código tengo harcode la parte de agregar a Column(), los Row(), ya que que la cantidad exacta que se tiene que agregar que son 6.

Seleccionar rango de días
Como mencione lineas atrás, cada celda que contiene un día del mes es de tipo FlatButton, por lo que este reaccionara al momento de hacer touch en cualquiera de ellos.
Dentro del código hay un método que se llama _celdButton, este metodo se encarga de generar cada boton de un numero del mes, y se configura para que reaccione al evento onPressed.
Dentro de los parámetros que recibe el elemento hay uno que se llama value, y no es mas que el valor del numero de día que le corresponde.
El evento onPressed dentro tiene implementada una function llamada: _onPressedceld, esta es una función declarada en la clase y que recibe el valor: value.
La función implementa un algoritmo que recrea el la vista cada vez que se toca una celda/día, pero con la diferencia de que, dentro del método, se aplican valores dependiendo de lo siguiente:
Primera vez que se toco una celda, se le pasa un valor a la variable _selectedRange, que es un listado de enteros List<int>, que determinara desde donde se pintara el rango de fechas. Para esta parte solo se agrega un valor.
Segunda vez, se determina si, la posición que se toco es mayor a la posicion que se tiene ya almacenada en _selectedRange, si es así quiere decir que se toco dos días uno menor y otro mayor, y se procede a pintar esos dias.
Posteriormente se dispara un evento llamado onRangeSelected, este pasa el rango de días seleccionado a un evento que se puede obtener por fuera del widget, para así quien lo implemente pueda obtener los valores seleccionados.
Nota: Lo anterior debido a la arquitectura de flutter en donde no puedo instaciar métodos o propiedades que pueda usar fuera del Widget, ya que es de tipo StatefulWidget y la lógica reside en una clase privada de tipo State, a la cual no se deberia poder acceder de ninguna manera mas que mediante el padre que es StatefulWidget.
Ejemplo: si la primera vez se toca el día 10 y después el día 12, entonces se pintan en la primera vuelta, el día 10 y en la segunda los días: 10,11,12.
Tercera vez, aquí lo que se hace es limpiar el vector con los valores seleccionados, para regenerar el widget sin días seleccionados, y después pasar el valor mediante onRangeSelected nuevamente para avisar que no hay valores seleccionados.
Aquí pueden ver una animación del calendario en flutter

Para pintar una sola celda, mando a llamar a la función _buildDesign, lo que hace esta función es recrear una y otra vez el widget completo para pintar una o mas celdas.
Nota: Estoy analizando la mejor manera de hacer esto, para no tener que crear cada vez el widget completo, aun que en celulares recientes no he tenido problemas de rendimiento.
Version 0.0.1
Hay mas cosas dentro del código que pase por alto aquí, que si las sigo comentando la publicación se haría aun mas extensa de lo que ya es, pero si les interesa saber, puede revisar el código, que esta publicado en github.
Esta es una version muy preliminar pero totalmente funcional, que espero mejorar en cuanto tenga la oportunidad.
También es el primer código que subo a pub.dev, este es el repositorio de paquetes de flutter.
Si quieres ver los parámetros para poder implementar el widget, revisa la documentación en pub.dev
Cosas a mejorar
Dentro de este proyecto hay muchas cosas a mejorar y refactorizar a nivel código.
- Código que no se utiliza. Hay partes de código y variables que pueden quitarse.
- Implementar los días de la semana, por lo menos en idioma ingles. El nombre del mes que se ve en la parte superior ya soporta la localización, no así los días, que esta hardcode.
- Implementar pintado de días por default. Esto es que si quieres que la vista se implemente con determinados días coloreados se pueda hacer.
- Bloque de días. Que se implemente con los días específicos bloqueados, esto es que no se puedan seleccionar.
- Buscar una mejor manera de pintar las celdas para no rehacer el widget cada vez.
Si se te ocurre una funcionalidad adicional, no dudes ponerla en los comentarios o en los issues de github, o mejor aun edita el código.
One thought on “Calendario en flutter. Paquete g_simple_calendar 0.0.1”