Day Scrolling for Month Scrolling

Asked

Viewed 518 times

-1

Good night!

I have this example of day scrolling that makes the user scroll through the dates of the month with each click both forward and backward. However, I would like that, instead of every click advance the day, be advanced the month forward or backward. It is possible?

Follow my code below:

page2.Dart

import 'package:flutter/material.dart';
import 'package:king_app/pages/calendar/scrolling.dart';
class Pagina2 extends StatelessWidget {

  DateTime selectedDate = DateTime.now();
  DateTime startDate = DateTime.now().subtract(Duration(days: 50));
  DateTime endDate = DateTime.now().add(Duration(days: 50));
  Map<String, Widget> widgets = Map();
  String widgetKeyFormat = "yyyy-MM-dd";
  
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: PreferredSize(
          preferredSize: Size.fromHeight(0),
          child: AppBar(
            backgroundColor: Color(0xFF11c76f),
            elevation: 0,
          ),
        ),
      body: Container(
        padding: EdgeInsets.only(top: 0),
        child: ScrollingDayCalendar(
          startDate: startDate,
          endDate: endDate,
          selectedDate: selectedDate,
          dateStyle: TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
          displayDateFormat: "dd/MM/yyyy",
          dateBackgroundColor: Color(0xFF11c76f),
          forwardIcon: Icons.arrow_forward,
          backwardIcon: Icons.arrow_back,
          pageChangeDuration: Duration(
            milliseconds: 700,
          ),
          widgets: widgets,
          widgetKeyFormat: widgetKeyFormat,
          noItemsWidget: Center(
            child: Text(
                "teste " ), 
          ),
        ),
      )
    );
  }
}

scrolling.Dart

library scrolling_day_calendar;

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

typedef ScrollingDayCalendarBuilder = Widget Function(
    BuildContext context,
    DateTime startDate,
    DateTime endDate,
    DateTime selectedDate,
    Function onDateChange,
    );

class ScrollingDayCalendar extends StatefulWidget {
  // first date on the pages
  final DateTime startDate;
  // last date on the pages
  final DateTime endDate;
  // the active date
  final DateTime selectedDate;
  // what to do then the date changes
  final Function onDateChange;
  // page widgets to display
  final Widget pageItems;
  // date format
  final String displayDateFormat;
  // date style
  final TextStyle dateStyle;
  // background color for date container
  final Color dateBackgroundColor;
  // forward icon
  final IconData forwardIcon;
  // back icon
  final IconData backwardIcon;
  // page change duration
  final Duration pageChangeDuration;

  final Map<String, Widget> widgets;
  final Widget noItemsWidget;
  final String widgetKeyFormat;

  ScrollingDayCalendar({
    @required this.pageItems,
    @required this.startDate,
    @required this.endDate,
    @required this.selectedDate,
    this.onDateChange,
    this.widgets,
    this.noItemsWidget,
    this.widgetKeyFormat,
    this.displayDateFormat,
    this.dateStyle,
    this.dateBackgroundColor,
    this.forwardIcon,
    this.backwardIcon,
    this.pageChangeDuration,
  });

  @override
  _ScrollingDayCalendarState createState() => _ScrollingDayCalendarState();
}

class _ScrollingDayCalendarState extends State<ScrollingDayCalendar> {
  PageController _pageController;
  int _totalPages;
  int _currentPage;
  int _previousPage;
  DateTime _selectedDate;

  _onPageChange(direction) {
    _currentPage = _pageController.page.round();

    if (_currentPage > _previousPage) {
      // went forward
      DateTime newDate = _selectedDate.add(
        Duration(days: 1),
      );

      setState(() {
        _selectedDate = newDate;
      });
    } else {
      // went back
      DateTime newDate = _selectedDate.subtract(
        Duration(days: 1),
      );

      setState(() {
        _selectedDate = newDate;
      });
    }

    _previousPage = _pageController.page.round();

    // run page update sent by user
    if (widget.onDateChange != null) {
      widget.onDateChange(direction, _selectedDate);
    }
  }

  Widget _buildPage(index) {
    if (widget.pageItems != null) {
      return widget.pageItems;
    }
    DateTime dateTime = widget.startDate;
    index = index + 1;

    dateTime = widget.startDate.add(Duration(days: index));
    String key = DateFormat(widget.widgetKeyFormat).format(dateTime);

    if (widget.widgets != null && widget.widgets.containsKey(key)) {
      return widget.widgets[key];
    }

    return widget.noItemsWidget;
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  void initState() {
    // set the selected date
    _selectedDate = widget.selectedDate;

    // calculate the start page
    int startingPage =
    _selectedDate.difference(widget.startDate).inDays.floor();

    setState(() {
      // set the total number of pages based on start date and end date
      _totalPages = widget.endDate.difference(widget.startDate).inDays.floor();

      // set starting page
      _pageController = PageController(initialPage: startingPage);

      // set previous page
      _previousPage = startingPage;
    });

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold (
      body: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              Container(
                height: 60.0,
                width: 60.0,
                color: widget.dateBackgroundColor != null
                    ? widget.dateBackgroundColor
                    : Colors.red,
                child: Center(
                  child: MaterialButton(
                    onPressed: () {
                      _pageController.previousPage(
                        duration: widget.pageChangeDuration != null
                            ? widget.pageChangeDuration
                            : Duration(microseconds: 700),
                        curve: Curves.easeIn,
                      );
                    },
                    child: Icon(
                      widget.backwardIcon == null
                          ? Icons.arrow_back
                          : widget.backwardIcon,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
              Expanded(
                child: Container(
                  height: 60.0,
                  color: widget.dateBackgroundColor != null
                      ? widget.dateBackgroundColor
                      : Colors.red,
                  child: Padding(
                    padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
                    child: Center(
                      child: Text(
                        DateFormat(widget.displayDateFormat != null
                            ? widget.displayDateFormat
                            : "MM/yyyy")
                            .format(_selectedDate),
                        style: widget.dateStyle != null
                            ? widget.dateStyle
                            : TextStyle(
                          fontWeight: FontWeight.w500,
                          color: Colors.white,
                          fontSize: 18.0,
                        ),
                      ),
                    ),
                  ),
                ),
              ),
              Container(
                height: 60.0,
                width: 60.0,
                color: widget.dateBackgroundColor != null
                    ? widget.dateBackgroundColor
                    : Colors.red,
                child: Center(
                  child: MaterialButton(
                    onPressed: () {
                      _pageController.nextPage(
                        duration: widget.pageChangeDuration != null
                            ? widget.pageChangeDuration
                            : Duration(milliseconds: 200),
                        curve: Curves.easeIn,
                      );
                    },
                    child: Icon(
                      widget.forwardIcon == null
                          ? Icons.arrow_forward
                          : widget.forwardIcon,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ],
          ),
          Expanded(
            child: PageView.builder(
              controller: _pageController,
              scrollDirection: Axis.horizontal,
              itemCount: _totalPages, // Can be null
              onPageChanged: (direction) => _onPageChange(direction),
              itemBuilder: (context, index) {
                return _buildPage(index);
              },
            ),
          ),
        ],
      )
    );
  }
}

inserir a descrição da imagem aqui

1 answer

0


One of the ways to do this is to create a new date when you click the "back" or "front" arrow".

Example:

Declare some attributes in your class to mount the date

// Depende da sua regra identificar a data inicial
// Aqui recuperei a data atual do dispositivo
DateTime data = DateTime.now();

int dia;
int mes;
int ano;
DateTime novaData;    

In initState() fill in the attributes of day, month and year using as reference the initial date you plan to use. Here in the case is based on the current date.

@override
void initState() {
  super.initState();

  dia = data.day;
  mes = data.month;
  ano = data.year;
}

Create a method to mount the date

_montarNovaData(){
  setState(() {
    novaData = DateTime(ano, mes, dia);    
  });
}

Within the button click function implement a small logic to sum up the day and year when you click the button > and another when the click is on the button < and in both make the request for the screen to be redesigned by calling the method of mounting the new date. As it has the setState() inside it the screen will be redesigned.

// Função que passa para o próximo mês
_clickProximoMes(){
  if(mes == 12){
    ano++;
    mes = 1;        
  } else {
    mes++;      
  }

  _montarNovaData();
}

// Função que passa para o mês anterior
_clickMesAnterior(){
  if(mes == 1){
    ano--;
    mes = 12;
  } else {                   
    mes--;
  }

  _montarNovaData();
}

With that novaData what you need is only use in the place that is mounting your Widget.

This is one of the ways to implement the desired behavior. I did not run the code in a project but it is a structure similar to an implementation I did in an app that is already in production and has worked smoothly for me.

On the logic of the next and previous month functions, could be improved by putting treatment for negative year for example or even limiting to a certain period but then everything is at your discretion.

The more I believe that with this it is possible that I make the adaptation to your code.

  • 1

    I managed to implement your solution, I just had to make a modification in the if because it was advanced both the month and the year, thank you very much!

  • @In fact, the logic of changing the year was incorrect. I edited the answer to be correct making the year change only when jump from December to January and January to December. Thanks for the feedback.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.