Related
I have a widget FilterSearchBar used in 2 others widgets. The first one HomeScreen contains a list of Team. In this one, I would like to display the full list of teams when the user clicks on the suffix icon in the searchbar. In the second one PlayerScreen I have a default Text widget and a list of Player appears when the user type something in the search bar and I would like to display the default Text widget when he clicks on the suffix icon. How can I do that ?
Here is the code.
Team
class Team {
final int id;
final String name;
Team({required this.id, required this.name});
static List<Team> getNFCTeam() => [
Team(id: 0, name: "Arizona Cardinals"),
Team(id: 1, name: "Atlanta Falcons"),
Team(id: 2, name: "Carolina Panthers"),
Team(id: 3, name: "Chicago Bears"),
Team(id: 4, name: "Dallas Cowboys"),
Team(id: 5, name: "Detroit Lions"),
Team(id: 6, name: "Green Bay Packers"),
Team(id: 7, name: "Los Angeles Rams"),
Team(id: 8, name: "Minnesota Vikings"),
Team(id: 9, name: "New Orleans Saints"),
Team(id: 10, name: "New York Giants"),
Team(id: 11, name: "Philadelphia Eagles"),
Team(id: 12, name: "San Francisco 49ers"),
Team(id: 13, name: "Seattle Seahawks"),
Team(id: 14, name: "Tampa Bay Buccaneers"),
Team(id: 15, name: "Washington Commanders"),
];
}
Player
class Player {
final int id;
final String name;
final number;
final position;
Player({required this.id, required this.name, required this.number, required this.position});
static List<Player> getPlayer() => [
Player(id: 0, name: "Brock Purdy", number: "#13", position: "QB"),
Player(id: 1, name: "Christian McCaffrey", number: "#23", position: "RB"),
Player(id: 2, name: "George Kittle", number: "#85", position: "TE"),
Player(id: 3, name: "Deebo Samuel", number: "#19", position: "WR"),
Player(id: 4, name: "Nick Bosa", number: "#97", position: "DL"),
Player(id: 5, name: "Fred Warner", number: "#54", position: "LB"),
];
}
FilterSearchBar
class FilterSearchBar extends StatefulWidget {
final String filterType;
final TextEditingController searchController;
final TextInputType keyboard;
final ValueChanged<String>? onChanged;
const FilterSearchBar({Key? key, required this.filterType, required this.searchController,
required this.keyboard, this.onChanged}) : super(key: key);
#override
State<FilterSearchBar> createState() => _FilterSearchBarState();
}
class _FilterSearchBarState extends State<FilterSearchBar> {
bool isClearClicked = false;
#override
void initState() {
widget.searchController.addListener(() {
setState(() {
isClearClicked = widget.searchController.text.isNotEmpty;
});
});
super.initState();
}
#override
void dispose() {
widget.searchController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: TextFormField(
keyboardType: widget.keyboard,
controller: widget.searchController,
onChanged: widget.onChanged,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(90),
),
borderSide: BorderSide(
color: Colors.grey,
),
),
prefixIcon: Icon(
Icons.search,
color: Colors.grey,
),
hintText: widget.filterType,
hintStyle: TextStyle(
color: Colors.grey,
fontSize: 16,
fontWeight: FontWeight.bold,
),
suffixIcon: isClearClicked
? IconButton(
onPressed: () {
clearSearchBar();
},
icon: Icon(
Icons.clear,
color: Colors.grey,
),
)
: null,
filled: true,
fillColor: Colors.white,
),
),
);
}
void clearSearchBar() {
widget.searchController.clear();
}
}
HomeScreen
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
TextEditingController searchController = TextEditingController();
List<Team> allTeams = Team.getNFCTeam();
List<Team> suggestions = Team.getNFCTeam();
List<Team> teams = Team.getNFCTeam();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("NFC"),
),
body: Column(
children: [
FilterSearchBar(
filterType: "Search team",
searchController: searchController,
keyboard: TextInputType.text,
onChanged: searchTeam,
),
showAllTeams(),
],
),
);
}
Widget showAllTeams() => Expanded(
child: ListView.builder(
itemCount: teams.length,
itemBuilder: (context, index) {
final team = teams[index];
return Column(
children: [
ListTile(
title: Padding(
padding: EdgeInsets.all(10),
child: Row(
children: [
Text(
team.name,
style: TextStyle(
color: Colors.black,
),
),
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PlayerScreen(team: team.name),
),
);
},
),
Divider(
height: 1,
color: Colors.grey,
),
],
);
},
),
);
void searchTeam(String query) {
suggestions = allTeams.where((team) {
final teamName = team.name.startsWith(query.toLowerCase());
return teamName;
}).toList();
setState(() => teams = suggestions.isEmpty ? allTeams : suggestions);
}
}
PlayerScreen
class PlayerScreen extends StatefulWidget {
final String team;
const PlayerScreen({Key? key, required this.team}) : super(key: key);
#override
State<PlayerScreen> createState() => _PlayerScreenState();
}
class _PlayerScreenState extends State<PlayerScreen> {
TextEditingController searchController = TextEditingController();
List<Player> allPayers = Player.getPlayer();
List<Player> suggestions = Player.getPlayer();
List<Player> players = Player.getPlayer();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.team),
),
body: Container(
color: Colors.grey,
child: Column(
children: [
FilterSearchBar(
filterType: "Search player",
searchController: searchController,
keyboard: TextInputType.text,
onChanged: searchPlayer,
),
searchController.text.isNotEmpty ? showAllPlayers() : Center(
child: Text(
"No player found",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
)
);
}
Widget showAllPlayers() => Expanded(
child: ListView.builder(
itemCount: players.length,
itemBuilder: (context, index) {
final player = players[index];
return Column(
children: [
ListTile(
title: Padding(
padding: EdgeInsets.all(10),
child: Row(
children: [
Text(
player.number,
style: TextStyle(
color: Colors.black,
),
),
const SizedBox(width: 5,),
Text(
player.position,
style: TextStyle(
color: Colors.black,
),
),
const SizedBox(width: 5,),
Text(
player.name,
style: TextStyle(
color: Colors.black,
),
),
const SizedBox(width: 5,),
],
),
),
),
Divider(
height: 1,
color: Colors.grey,
),
],
);
},
),
);
void searchPlayer(String query) {
suggestions = allPayers.where((player) {
final playerName = player.name.startsWith(query.toLowerCase());
return playerName;
}).toList();
setState(() => players = suggestions.isEmpty ? allPayers : suggestions);
}
}
I'm using the next piece of code to render an item for a Dropdown component on Flutter web:
Widget _getColorInfo(int color, String text) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Color(color),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(width: 5),
Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
],
);
}
Now the problem is that if I do not use Flex or Expanded the widget will not recognize the overflow:
Overflow on the selected item
And the dropdown is working:
Overflow on the expanded panel
Now, if I use Flex the overflow is now recognized by the main component:
Flexible(
child: Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
),
Overlfow handled on the selected item
But now the dropdown is not working since it detects the 'size' property is missing:
Error on the expanded panel of the dropdown
Here is the error:
Image of the error
The problem is that I can't use the size of the screen to determine how much width is the component taking since all the other components are being expanded dynamically, so it will break the UI symmetry. Is there any other way to control the overflow when using a dropdown or something to differentiate the dropdown panel from the selected item so I can apply different strategies like using a fixed width for the dropdown panel and a Flex for the selected item?
I have already tried using Flex, Expanded, try catch, Wrap, ConstrainedBox, third party packages... nothing solved the problem.
UPDATE
Here is the full code of the component:
class ColorSelect extends StatelessWidget {
final AgendaFormHandler form;
final int? defaultColor;
final Map<String, int> elements = {
'Team 1': 0xFFFEC000,
'Team 2': 0xFF65CBFD,
'Team 3': 0xFFF2B085,
'Team 4': 0xFF01AE51,
'USO': 0xFF3764F7,
'Financiero': 0xFF702FA0,
'Procesos': 0xFF8FA9DA,
'TI': 0xFFFF7C81,
'Alterno 1': 0xFF00F3EB,
'Alterno 2': 0xFFA46B6B,
'Alterno 3': 0xFFDCBFB2,
'Alterno 4': 0xFFEF5CEF,
'Alterno 5': 0xFFC5D89F,
};
ColorSelect({super.key, required this.form, this.defaultColor});
#override
Widget build(BuildContext context) {
return SimpleSelectInput<int>(
title: 'Color',
customEmptyText: 'Ninguno',
activeOption: form.getValue<int?>('color') ?? defaultColor ?? 0xFFFEC000,
onOptionSelected: (color) {
form.updateValue('color', color);
},
options: List<SelectItem<int>>.from(elements.keys.map((key) {
return SelectItem(
value: elements[key]!,
text: key,
label: _getColorInfo(elements[key]!, key),
);
})),
);
}
Widget _getColorInfo(int color, String text) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Color(color),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(width: 5),
Flexible(
fit: FlexFit.tight,
child: Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
),
],
);
}
}
Here is the code of the SimpleSelectInput
/// A class to create a new option entry for the [SimpleSelectInput]
/// component with [T] as the datatype for the [value] and [String]
/// for the [label]
class SelectItem<T> {
final T value;
/// This text is only to be able to compare the content of the
/// input when applying any search filter. So, the same you put
/// in the [label] property is the one you should put here.
final String text;
final Widget? label;
SelectItem({
required this.value,
this.label,
required this.text,
});
}
/// A simple input with a bottom border and a blue title with
/// [T] as the value datatype for the dropdown options.
class SimpleSelectInput<T> extends StatelessWidget {
/// The options to be displayed in the dropdown
final List<SelectItem<T>> options;
/// The item to be rendered as the default selected item
final T activeOption;
final Function(T?) onOptionSelected;
final String Function(T?)? validator;
final String? title;
/// The text to be displayed when no option is selected
final String? customEmptyText;
final bool shouldDecorate;
final bool showSearchBox;
const SimpleSelectInput({
super.key,
required this.options,
required this.activeOption,
required this.onOptionSelected,
this.title,
this.validator,
this.shouldDecorate = true,
this.showSearchBox = false,
this.customEmptyText = '-- Seleccione --',
});
#override
Widget build(BuildContext context) {
if (activeOption == null) {
return Container();
}
// If there is no opportunity it will raise an error.
// TODO: check if it's ok to raise the BadState error.
late final SelectItem<T>? selectedOption;
try {
selectedOption = options.firstWhere(
(SelectItem<T> option) {
return option.value == activeOption;
},
);
} catch (_) {
selectedOption = null;
}
return DropdownSearch<SelectItem<T>>(
items: options,
selectedItem: selectedOption,
filterFn: (item, filter) {
return item.text.toLowerCase().contains(filter.toLowerCase());
},
dropdownButtonProps: const DropdownButtonProps(
focusColor: ColorStyle.blue,
color: Colors.black,
icon: Icon(
Icons.arrow_drop_down,
size: ShapeStyle.standardFontSize * 1.4,
),
),
dropdownBuilder: (context, value) {
return value?.label ??
Text(
customEmptyText ?? '-- Seleccione --',
style: const TextStyle(
fontSize: ShapeStyle.standardFontSize,
),
);
},
validator: (selected) {
return validator?.call(selected?.value);
},
popupProps: PopupPropsMultiSelection.menu(
showSearchBox: showSearchBox,
showSelectedItems: true,
itemBuilder: _itemBuilder,
searchFieldProps: const TextFieldProps(
scrollPadding: EdgeInsets.zero,
style: TextStyle(
color: ColorStyle.blue,
fontSize: ShapeStyle.standardFontSize,
),
),
),
compareFn: (i, s) => i.value == s.value,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
fillColor: Colors.black,
border: shouldDecorate ? null : InputBorder.none,
labelStyle: const TextStyle(
color: ColorStyle.blue,
fontSize: ShapeStyle.standardFontSize * 1.4,
),
label: title != null
? Text(
title!,
style: const TextStyle(
fontSize: ShapeStyle.standardFontSize * 1.4,
),
)
: null,
),
),
onChanged: (x) => onOptionSelected(x?.value),
);
}
Widget _itemBuilder(
BuildContext context, SelectItem<T> item, bool isSelected) {
final size = MediaQuery.of(context).size;
return Container(
width: size.width,
padding: const EdgeInsets.symmetric(
vertical: 12.0,
horizontal: 8.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
item.label ??
Text(
item.text,
style: const TextStyle(
color: Colors.black,
fontSize: ShapeStyle.standardFontSize,
),
),
// a check icon
if (isSelected)
const Icon(
Icons.check,
color: Colors.green,
size: ShapeStyle.standardIconSize,
),
],
),
);
}
}
I am trying to make a simple calendar app in Flutter. He had no problems installing the dependencies, but when he tried to add events he couldn't. It used Date.Time.now with no problem, but when it tried to do it with a specific date it failed. I would appreciate someone who could help me. Thanks a lot
import 'package:flutter_clean_calendar/flutter_clean_calendar.dart';
class MoonCalendar extends StatefulWidget {
#override
_MoonCalendarState createState() => _MoonCalendarState();
}
class _MoonCalendarState extends State<MoonCalendar> {
DateTime selectedDay;
List <CleanCalendarEvent> selectedEvent;
final Map<DateTime,List<CleanCalendarEvent>> events = {
DateTime (DateTime.utc(2022).year,DateTime.utc(07).month,DateTime.utc(15).day):
[
CleanCalendarEvent(
'Event A',
startTime: DateTime(
DateTime.utc(2022).year,DateTime.utc(7).month,DateTime.utc(15).day,10,0),
endTime: DateTime(
DateTime.utc(2022).year,DateTime.utc(7).month,DateTime.utc(15).day,12,0),
description: 'A special event',
color: Colors.blue[700]),
],
};
void _handleData(date){
setState(() {
selectedDay = date;
selectedEvent = events[selectedDay] ?? [];
});
print(selectedDay);
}
#override
void initState() {
// TODO: implement initState
selectedEvent = events[selectedDay] ?? [];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Calendario",
style: TextStyle(fontSize: 22),
),
Text(
" Lunar",
style: TextStyle(fontSize: 22, color: Color.fromRGBO(56, 215, 199, 1)),
)
],
),
),
body: SafeArea(
child: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Color.fromRGBO(61, 138, 146, 0.2),
Color.fromRGBO(56, 215, 199, 0.1)
]
)
),
child: Calendar(
startOnMonday: true,
selectedColor: Colors.blue,
todayColor: Colors.red,
eventColor: Colors.green,
eventDoneColor: Colors.amber,
bottomBarColor: Colors.deepOrange,
onRangeSelected: (range) {
print('selected Day ${range.from},${range.to}');
},
onDateSelected: (date){
return _handleData(date);
},
events: events,
isExpanded: true,
dayOfWeekStyle: TextStyle(
fontSize: 15,
color: Colors.black12,
fontWeight: FontWeight.w100,
),
bottomBarTextStyle: TextStyle(
color: Colors.black87,
),
hideBottomBar: false,
hideArrows: false,
weekDays: ['Lun','Mar','Mié','Jue','Vie','Sáb','Dom'],
),
),
),
);
}
} ```
NOTE: The code may seem very long, but for this question you don't need to understand every part of it.
I have an app, which gets data from an API to build a chart with it. I use the Syncfusion cartesian chart package. This is an economic indicator, so it brings a date and a value, for example:
[[2015 Oct, 0.24],[2015 Nov, 0.26],[2015 Dec, 0.32],[2016 Jan, 0.35],[2016 Feb, 0.40],[2016 Mar, 0.48]]
So, once the data arrives (It has a loading screen for waiting the data form the HTTP request), I build the chart with it.
So in this case, my Parent widget is named ChartScreen. Here's the code:
class ChartScreen extends StatefulWidget {
#override
State<ChartScreen> createState() => _ChartScreenState();
}
class _ChartScreenState extends State<ChartScreen> {
String dropdownValue = '';
initState() {
dropdownValue = '2016';
return super.initState();
}
#override
Widget build(BuildContext context) {
final enterpriseProvider = Provider.of<EnterpriseProvider>(context);
final resp = enterpriseProvider.indicator;
List<IpcData> data = _createIpcList(resp, dropdownValue);
if( data.length == 0 ) {
return Scaffold(
appBar: AppBar(
title: Text('Obteniendo datos...'),
),
body: Container(
color: Colors.black,
width: double.infinity,
height: double.infinity,
child: Center(
child: CircularProgressIndicator(),
),
),
);
}
return
Scaffold(
appBar: AppBar(
title: Text('IPC'),
actions:[
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.white,
),
onChanged: (String? newValue) {
dropdownValue = newValue!;
data = _createIpcList(resp, dropdownValue);
setState(() {});
},
items: <String>['2016', '2017', '2018', '2019']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList()
),
)
]
),
drawer: SideMenu(),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
child: ResultChart( formattedData: data )###############################
),
),
],
)
);
}
_createIpcList(List<List<dynamic>> resp, [String? year]) {
print('EL AÑOO');
print(year);
List<IpcData>finalList = [];
if(resp.length != 0) {
for(int i = 0; i < resp.length; i++) {
try {
resp[i][0] = DateFormat.yMMM().format(DateTime.parse(resp[i][0]));
} catch(e) {}
}
}
List<IpcData> ipcList = resp.map((e) => IpcData(e[0], e[1])).toList();
if (year!= null) {
for(int i = 0; i < ipcList.length; i++){
if (ipcList[i].date.contains(year)){
finalList.add(ipcList[i]);
}
}
}
return finalList;
}
}
With the _createIpcList I format the JSON data, so the chart can use it. I highlighted the line in which I call the child whose state I want to update. But before that, you can se that I added a dropdown menu, to select a year from a (hardcoded) list. When the dropdown menu selected item changes (see onChanged), I call the SetState and pass the 'year parameter' to the _createIpcList, which filters the data and returns the items that belong to the selected year. Here's the child code:
class ResultChart extends StatefulWidget {
final List<IpcData> formattedData;
const ResultChart({
Key? key,
required this.formattedData
}) : super(key: key);
#override
_ResultChartState createState() => _ResultChartState();
}
class _ResultChartState extends State<ResultChart> {
late List<IpcData> _chartData;
#override
void initState() {
_chartData = widget.formattedData;
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: SfCartesianChart(
backgroundColor: Colors.black,
enableAxisAnimation: false,
trackballBehavior: TrackballBehavior(
enable: true,
shouldAlwaysShow: true,
tooltipSettings: InteractiveTooltip(
borderWidth: 2,
borderColor: Colors.grey,
color: Colors.grey[400],
format: 'point.x : point.y'
)
),
zoomPanBehavior: ZoomPanBehavior(
enablePanning: true,
enablePinching: true,
enableDoubleTapZooming: true,
zoomMode: ZoomMode.xy,
),
primaryXAxis: CategoryAxis(
labelRotation: 90,
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey[400]
),
axisLine: AxisLine(
width: 2,
color: Colors.grey
),
majorGridLines: MajorGridLines(width: 1),
),
primaryYAxis: NumericAxis(
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey[400]
),
axisLine: AxisLine(
width: 2,
color: Colors.grey
),
title: AxisTitle( text: 'IPC', textStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
majorGridLines: MajorGridLines(width: 1),
),
series: <ChartSeries>[
LineSeries<IpcData, String>(
color: Colors.blue,
dataSource: _chartData,
xValueMapper: (IpcData data, _) => data.date,
yValueMapper: (IpcData data, _) => data.value
)
],)
);
}
}
class IpcData {
final String date;
final double value;
IpcData(this.date, this.value);
}
My problem is that, no matter which year I select, the chart doesn't change. I know that the 'dropdownValue' changes because I debugged with some prints() but I don´t know how to rebuild or set state of the ResultChart widget.
Well it turn out that I continued debugging, and actually the ResultChart widget was being rebuilt again and again, but I never called the setState function inside the children. Beginner error I know, but I'm new with Flutter.
I want to mark the event once every 4 days.
example today is the 1st, mark the event will be available on the 5th, 10th, 15th, 20th, 25th, 30th, etc.
if today is the 3rd, the event will be available on the 8th, 13th, 18th, etc.
how does that function work?
I use this calendar plugin
https://pub.dev/packages/flutter_calendar_carousel
Following is the function to mark event manually:
EventList<Event> _markedDateMap = new EventList<Event>();
build(){
_calendarCarouselNoHeader = CalendarCarousel<Event>(
...
markedDatesMap: _markedDateMap,
...
),
}
#override
void initState() {
_markedDateMap.add(
new DateTime(2020, 2, 26),
new Event(
date: new DateTime(2020, 2, 26),
title: 'Event 5',
icon: _eventIcon,
));
_markedDateMap.add(
new DateTime(2020, 2, 26),
new Event(
date: new DateTime(2020, 2, 26),
title: 'Event 5',
icon: _eventIcon,
));
super.initState();
}
Any anwser will appreciated.
You can copy paste run full code below
working demo show when pass start date time with
addMarker(DateTime(2020, 2, 01));
addMarker(DateTime(2020, 2, 03));
code snippet
addMarker(DateTime startEventDateTime) {
var eventDateTime = startEventDateTime;
for(int i=0; i<5; i++) {
if(eventDateTime.day == 1) {
eventDateTime = eventDateTime.add(Duration(days: (4)));
} else {
eventDateTime = eventDateTime.add(Duration(days: (5)));
}
print(eventDateTime.toLocal());
_markedDateMap.add(
eventDateTime,
Event(
date: eventDateTime,
title: 'Event $i',
icon: _eventIcon,
));
}
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart'
show CalendarCarousel;
import 'package:flutter_calendar_carousel/classes/event.dart';
import 'package:flutter_calendar_carousel/classes/event_list.dart';
import 'package:intl/intl.dart' show DateFormat;
void main() => runApp( MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'dooboolab flutter calendar',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Calendar Carousel Example'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DateTime _currentDate = DateTime(2020, 2, 17);
DateTime _currentDate2 = DateTime(2020, 2, 17);
String _currentMonth = DateFormat.yMMM().format(DateTime(2020, 2, 17));
DateTime _targetDateTime = DateTime(2020, 2, 17);
// List<DateTime> _markedDate = [DateTime(2018, 9, 20), DateTime(2018, 10, 11)];
static Widget _eventIcon = Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(1000)),
border: Border.all(color: Colors.blue, width: 2.0)),
child: Icon(
Icons.person,
color: Colors.amber,
),
);
EventList<Event> _markedDateMap = EventList<Event>();
CalendarCarousel _calendarCarousel, _calendarCarouselNoHeader;
#override
void initState() {
addMarker(DateTime(2020, 2, 03));
super.initState();
}
addMarker(DateTime startEventDateTime) {
var eventDateTime = startEventDateTime;
for(int i=0; i<5; i++) {
if(eventDateTime.day == 1) {
eventDateTime = eventDateTime.add(Duration(days: (4)));
} else {
eventDateTime = eventDateTime.add(Duration(days: (5)));
}
print(eventDateTime.toLocal());
_markedDateMap.add(
eventDateTime,
Event(
date: eventDateTime,
title: 'Event $i',
icon: _eventIcon,
));
}
}
#override
Widget build(BuildContext context) {
/// Example with custom icon
_calendarCarousel = CalendarCarousel<Event>(
onDayPressed: (DateTime date, List<Event> events) {
this.setState(() => _currentDate = date);
events.forEach((event) => print(event.title));
},
weekendTextStyle: TextStyle(
color: Colors.red,
),
thisMonthDayBorderColor: Colors.grey,
// weekDays: null, /// for pass null when you do not want to render weekDays
headerText: 'Custom Header',
// markedDates: _markedDate,
weekFormat: true,
markedDatesMap: _markedDateMap,
height: 200.0,
selectedDateTime: _currentDate2,
showIconBehindDayText: true,
// daysHaveCircularBorder: false, /// null for not rendering any border, true for circular border, false for rectangular border
customGridViewPhysics: NeverScrollableScrollPhysics(),
markedDateShowIcon: true,
markedDateIconMaxShown: 2,
selectedDayTextStyle: TextStyle(
color: Colors.yellow,
),
todayTextStyle: TextStyle(
color: Colors.blue,
),
markedDateIconBuilder: (event) {
return event.icon;
},
minSelectedDate: _currentDate.subtract(Duration(days: 360)),
maxSelectedDate: _currentDate.add(Duration(days: 360)),
todayButtonColor: Colors.transparent,
todayBorderColor: Colors.green,
markedDateMoreShowTotal:
false, // null for not showing hidden events indicator
// markedDateIconMargin: 9,
// markedDateIconOffset: 3,
);
/// Example Calendar Carousel without header and custom prev & next button
_calendarCarouselNoHeader = CalendarCarousel<Event>(
todayBorderColor: Colors.green,
onDayPressed: (DateTime date, List<Event> events) {
this.setState(() => _currentDate2 = date);
events.forEach((event) => print(event.title));
},
daysHaveCircularBorder: true,
showOnlyCurrentMonthDate: false,
weekendTextStyle: TextStyle(
color: Colors.red,
),
thisMonthDayBorderColor: Colors.grey,
weekFormat: false,
// firstDayOfWeek: 4,
markedDatesMap: _markedDateMap,
height: 420.0,
selectedDateTime: _currentDate2,
targetDateTime: _targetDateTime,
customGridViewPhysics: NeverScrollableScrollPhysics(),
markedDateCustomShapeBorder: CircleBorder(
side: BorderSide(color: Colors.yellow)
),
markedDateCustomTextStyle: TextStyle(
fontSize: 18,
color: Colors.blue,
),
showHeader: false,
// markedDateIconBuilder: (event) {
// return Container(
// color: Colors.blue,
// );
// },
todayTextStyle: TextStyle(
color: Colors.blue,
),
todayButtonColor: Colors.yellow,
selectedDayTextStyle: TextStyle(
color: Colors.yellow,
),
minSelectedDate: _currentDate.subtract(Duration(days: 360)),
maxSelectedDate: _currentDate.add(Duration(days: 360)),
prevDaysTextStyle: TextStyle(
fontSize: 16,
color: Colors.pinkAccent,
),
inactiveDaysTextStyle: TextStyle(
color: Colors.tealAccent,
fontSize: 16,
),
onCalendarChanged: (DateTime date) {
this.setState(() {
_targetDateTime = date;
_currentMonth = DateFormat.yMMM().format(_targetDateTime);
});
},
onDayLongPressed: (DateTime date) {
print('long pressed date $date');
},
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//custom icon
Container(
margin: EdgeInsets.symmetric(horizontal: 16.0),
child: _calendarCarousel,
), // This trailing comma makes auto-formatting nicer for build methods.
//custom icon without header
Container(
margin: EdgeInsets.only(
top: 30.0,
bottom: 16.0,
left: 16.0,
right: 16.0,
),
child: Row(
children: <Widget>[
Expanded(
child: Text(
_currentMonth,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24.0,
),
)),
FlatButton(
child: Text('PREV'),
onPressed: () {
setState(() {
_targetDateTime = DateTime(_targetDateTime.year, _targetDateTime.month -1);
_currentMonth = DateFormat.yMMM().format(_targetDateTime);
});
},
),
FlatButton(
child: Text('NEXT'),
onPressed: () {
setState(() {
_targetDateTime = DateTime(_targetDateTime.year, _targetDateTime.month +1);
_currentMonth = DateFormat.yMMM().format(_targetDateTime);
});
},
)
],
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16.0),
child: _calendarCarouselNoHeader,
), //
],
),
));
}
}