Skip to content

Instantly share code, notes, and snippets.

@chaudharydeepanshu
Last active August 5, 2022 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chaudharydeepanshu/c1e3fe0386cb464514f62b73305962ee to your computer and use it in GitHub Desktop.
Save chaudharydeepanshu/c1e3fe0386cb464514f62b73305962ee to your computer and use it in GitHub Desktop.
data_grid_jank_example
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(
useMaterial3: true,
),
darkTheme: ThemeData.dark(
useMaterial3: true,
),
themeMode: ThemeMode.system,
home: const Home(),
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late PageController _controller;
// This points always to the mid-element in _list
late int _initialIndex;
// This should work with 3, 7, 11, ... odd elements. Mind the pattern!!!
List<int> list = [-2, -1, 0, 1, 2];
@override
void initState() {
super.initState();
// Calculate mid.
_initialIndex = (list.length / 2).floor();
_controller =
PageController(initialPage: _initialIndex, viewportFraction: 1);
// This is where we listen to changes.
_controller.addListener(() {
// Get index according to the direction
// _controller.page! > _initialIndex => swiping to the right, going to the left / previous element
// _controller.page! < _initialIndex => swiping to the left, going to the right / next element
final index = _controller.page! > _initialIndex
? _controller.page!.floor()
: _controller.page!.ceil();
if (index == _initialIndex) return;
if (index == _initialIndex - 1) {
_prev();
} else if (index == _initialIndex + 1) {
_next();
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
// Update list and jump to the middle element
void _next() {
setState(() {
list
..removeAt(0)
..insert(list.length, list.last + 1);
// Update current DateTime here
});
_controller.jumpToPage(_initialIndex);
}
// Update list and jump to the middle element
void _prev() {
setState(() {
list
..insert(0, list.first - 1)
..removeLast();
// Update current DateTime here
});
_controller.jumpToPage(_initialIndex);
}
List months = [
'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
'sep',
'oct',
'nov',
'dec'
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 2,
title: const Text("Data Grid"),
),
body: PageView.builder(
controller: _controller,
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
DateTime gridDateTime = DateTime(2022, 8 + list[index], 1);
List<DateTime> allDatesOfCalendarMonth =
getDatesForACalendarMonthAsUTC(dateTime: gridDateTime);
return Column(
children: [
Text(months[gridDateTime.month - 1] +
" " +
gridDateTime.year.toString()),
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 1,
crossAxisCount: 7,
mainAxisExtent: 40,
),
itemCount: 42,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, i) {
return DataWidget(
dataDateTime: allDatesOfCalendarMonth[i],
gridDateTime: gridDateTime,
);
},
),
],
);
}),
);
}
}
class DataWidget extends StatelessWidget {
const DataWidget(
{Key? key, required this.dataDateTime, required this.gridDateTime})
: super(key: key);
final DateTime dataDateTime;
final DateTime gridDateTime;
@override
Widget build(BuildContext context) {
return Row(
children: [
const Expanded(
child: SizedBox(),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
minimumSize: Size.zero,
padding: EdgeInsets.zero,
foregroundColor: Theme.of(context).colorScheme.onSurface,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
side: BorderSide.none,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: dataDateTime.month == gridDateTime.month
? () {
//do something
}
: null,
child: SizedBox(
width: 40,
child: Container(
margin: const EdgeInsets.all(4.0),
child: Ink(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).dividerColor, width: 1),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Center(
child: Text(
dataDateTime.day.toString(),
style: const TextStyle(),
),
),
),
),
),
),
const Expanded(
child: SizedBox(),
),
],
);
}
}
List<DateTime> getDatesForACalendarMonthAsUTC({required DateTime dateTime}) {
List<DateTime> calendarMonthDaysAsUTC = [];
DateTime currentDateTime = DateTime.utc(dateTime.year, dateTime.month, 1);
DateTime firstDayOfMonthAsUTC =
DateTime.utc(currentDateTime.year, currentDateTime.month, 1);
DateTime lastDayOfMonthAsUTC =
getLastDayOfAMonth(currentDateTime: currentDateTime);
List<DateTime> datesFirstToLastDayOfMonthAsUTC =
getDaysInBetweenIncludingStartEndDate(
startDateTime: firstDayOfMonthAsUTC,
endDateTime: lastDayOfMonthAsUTC);
calendarMonthDaysAsUTC = List.generate(datesFirstToLastDayOfMonthAsUTC.length,
(index) => datesFirstToLastDayOfMonthAsUTC[index]);
int firstDayOfMonthWeekDay = firstDayOfMonthAsUTC.weekday;
for (int i = 1;
i <= firstDayOfMonthWeekDay && firstDayOfMonthWeekDay != 7;
i++) {
calendarMonthDaysAsUTC.insert(
0, firstDayOfMonthAsUTC.subtract(Duration(days: i)));
}
int daysLeftAfterMonthEndDate = 42 - calendarMonthDaysAsUTC.length;
for (int i = 1; i <= daysLeftAfterMonthEndDate; i++) {
calendarMonthDaysAsUTC.add(lastDayOfMonthAsUTC.add(Duration(days: i)));
}
return calendarMonthDaysAsUTC;
}
List<DateTime> getDaysInBetweenIncludingStartEndDate(
{required DateTime startDateTime, required DateTime endDateTime}) {
// Converting dates provided to UTC
// So that all things like DST don't affect subtraction and addition on dates
DateTime startDateInUTC =
DateTime.utc(startDateTime.year, startDateTime.month, startDateTime.day);
DateTime endDateInUTC =
DateTime.utc(endDateTime.year, endDateTime.month, endDateTime.day);
// Created a list to hold all dates
List<DateTime> daysInFormat = [];
// Starting a loop with the initial value as the Start Date
// With an increment of 1 day on each loop
// With condition current value of loop is smaller than or same as end date
for (DateTime i = startDateInUTC;
i.isBefore(endDateInUTC) || i.isAtSameMomentAs(endDateInUTC);
i = i.add(const Duration(days: 1))) {
// Converting back UTC date to Local date before inserting in list
// You can keep in UTC format depending on your case
if (startDateTime.isUtc) {
daysInFormat.add(i);
} else {
daysInFormat.add(DateTime(i.year, i.month, i.day));
}
}
return daysInFormat;
}
DateTime getLastDayOfAMonth({required DateTime currentDateTime}) {
// Getting the 15th-day date of the month for the date provided
DateTime fifteenthDayOfMonth =
DateTime(currentDateTime.year, currentDateTime.month, 15);
// Converting the 15th-day date to UTC
// So that all things like DST don't affect subtraction and addition on date
DateTime twentiethDayOfMonthInUTC = fifteenthDayOfMonth.toUtc();
// Getting a random date of next month by adding 20 days to twentiethDayOfMonthInUTC
// Adding number 20 to any month 15th-day will definitely give a next month date
DateTime nextMonthRandomDateInUTC =
twentiethDayOfMonthInUTC.add(const Duration(days: 20));
DateTime nextMonthRandomDateZeroDayInUTC = DateTime.utc(
nextMonthRandomDateInUTC.year, nextMonthRandomDateInUTC.month, 0);
// Now getting the 0th day date of the next month
// This will give us the current month last date
DateTime nextMonthRandomDateZeroDayInLocal = DateTime(
nextMonthRandomDateInUTC.year, nextMonthRandomDateInUTC.month, 0);
DateTime lastDayOfAMonth;
if (currentDateTime.isUtc) {
lastDayOfAMonth = nextMonthRandomDateZeroDayInUTC;
} else {
lastDayOfAMonth = nextMonthRandomDateZeroDayInLocal;
}
return lastDayOfAMonth;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment