I am trying to use riverpod inside my modalBottomsheet to update values, but when i try to update the state, it updates but Widget(FilterView) doesnot rebuild.
Since it is Inside of showModalBottomSheet, I have enclosed it inside
ProviderScope()
Below is my code to open ModalBottomSheet
_showFilter(BuildContext context) {
final container = ProviderScope.containerOf(context);
showModalBottomSheet(
context: context,
builder: (context) => ProviderScope(
parent: container,
child: FilterView(
key: Key("filterview"),
),
),
);
}
Below is FilterView Widget
class FilterView extends ConsumerWidget {
const FilterView({super.key});
#override
Widget build(BuildContext context, WidgetRef ref) {
return Consumer(builder: ((context, ref, _) {
var reference = ref.read(filterController.notifier);
var filter = ref.watch(filterController);
return Card(
child: Column(
children: [
const Text(
"FILTER",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Checkbox(
value: filter.today,
onChanged: (bool? value) {
reference.today(value ?? false);
}),
const Text("Today only")
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("From Date:"),
IconButton(
onPressed: filter.today
? null
: () async {
var date =
await _showDatePicker(context, filter.fromDate);
reference.newFromDate(date);
},
icon: const Icon(Icons.date_range),
),
if (filter.fromDate != null)
Text(filter.fromDate!.toHumanRedable()),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("To Date"),
IconButton(
onPressed: filter.today
? null
: () async {
var date =
await _showDatePicker(context, filter.toDate);
reference.newToDate(date);
},
icon: const Icon(Icons.date_range),
),
if (filter.toDate != null)
Text(filter.toDate!.toHumanRedable()),
],
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("Filter with Given Criteria"),
),
],
),
);
}));
}
Future<DateTime?> _showDatePicker(
BuildContext context, DateTime? initialDate) async {
var result = await showDatePicker(
context: context,
firstDate: DateTime.now().add(const Duration(days: -100)),
lastDate: DateTime.now().add(const Duration(days: 100)),
initialDate: initialDate ?? DateTime.now(),
);
return result;
}
}
State does update I have verified that. But FilterView Widget is not rebuilding.
Below is the provider
final filterController =
StateNotifierProvider<FilterNotifier, Filter>((ref) => FilterNotifier());
class FilterNotifier extends StateNotifier<Filter> {
FilterNotifier() : super(Filter.initialize());
void newFromDate(DateTime? fromDateTime) {
state = state.registerNewFromDate(fromDateTime);
}
void newToDate(DateTime? toDateTime) {
state = state.registerNewToDate(toDateTime);
}
void today(bool today) {
state = state.registerNewValues(today, state.fromDate, state.toDate);
}
}
Below is a filter class
class Filter {
bool today = true;
DateTime? fromDate;
DateTime? toDate;
Filter._(this.today, this.fromDate, this.toDate);
static Filter initialize() {
return Filter._(true, null, null);
}
Filter registerNewValues(bool today, DateTime? fromDate, DateTime? toDate) {
this.today = today;
this.fromDate = fromDate;
this.toDate = toDate;
return this;
}
Filter registerNewFromDate(DateTime? fromDate) {
this.fromDate = fromDate;
return this;
}
Filter registerNewToDate(DateTime? toDate) {
this.toDate = toDate;
return this;
}
}
The current model class Filter having same instance, therefore the UI isn't updating. To update the state, you need to pass new instance. I am creating copyWith method wich is handy,
class Filter { // I pefer creating final fileds with copyWith
bool today = true;
DateTime? fromDate;
DateTime? toDate;
Filter._(this.today, this.fromDate, this.toDate);
static Filter initialize() {
return Filter._(true, null, null);
}
Filter registerNewFromDate(DateTime? fromDate) {
this.fromDate = fromDate;
return this;
}
Filter registerNewToDate(DateTime? toDate) {
this.toDate = toDate;
return this;
}
Filter copyWith(
bool? today,
DateTime? fromDate,
DateTime? toDate,
) {
return Filter._(
today ?? this.today,
fromDate ?? this.fromDate,
toDate ?? this.toDate,
);
}
}
Related
I am working on a flutter app where I want to show date and time pickers. In the third example, I am trying to show how to pick a date and time together, which is working fine. But what I want is when the user cancels the date picker dialog then the time picker should automatically get cancelled. But right now we have to do it separately. For reference, you can see the problem in the image below.
I need some help with the app logic to cancel the date & time picked at once.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const DateTimePickerApp());
}
class DateTimePickerApp extends StatelessWidget {
const DateTimePickerApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: TextScreen(),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({
Key? key,
}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay.now();
DateTime dateTime = DateTime.now();
bool showDate = true;
bool showTime = true;
bool showDateTime = true;
// Select for Date
Future<DateTime> _selectDate(BuildContext context) async {
final selected = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2000),
lastDate: DateTime(2025),
);
if (selected != null && selected != selectedDate) {
setState(() {
selectedDate = selected;
});
}
return selectedDate;
}
// Select for Time
Future<TimeOfDay> _selectTime(BuildContext context) async {
final selected = await showTimePicker(
context: context,
initialTime: selectedTime,
);
if (selected != null && selected != selectedTime) {
setState(() {
selectedTime = selected;
});
}
return selectedTime;
}
// select date time picker
Future _selectDateTime(BuildContext context) async {
final date = await _selectDate(context);
final time = await _selectTime(context);
if (date == null) return;
if (time == null) return;
setState(() {
dateTime = DateTime(
date.year,
date.month,
date.day,
time.hour,
time.minute,
);
});
}
String getDate() {
// ignore: unnecessary_null_comparison
if (selectedDate == null) {
return 'select date';
} else {
return DateFormat('MMM d, yyyy').format(selectedDate);
}
}
String getDateTime() {
// ignore: unnecessary_null_comparison
if (dateTime == null) {
return 'select date timer';
} else {
return DateFormat('yyyy-MM-dd HH:mm a').format(dateTime);
}
}
String getTime(TimeOfDay tod) {
final now = DateTime.now();
final dt = DateTime(now.year, now.month, now.day, tod.hour, tod.minute);
final format = DateFormat.jm();
return format.format(dt);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GeeksforGeeks'),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
showDate ? Center(child: Text(getDate())) : const SizedBox(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton(
onPressed: () {
_selectDate(context);
showDate = true;
},
child: const Text('Date Picker'),
),
),
showTime
? Center(child: Text(getTime(selectedTime)))
: const SizedBox(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton(
onPressed: () {
_selectTime(context);
showTime = true;
},
child: const Text('Timer Picker'),
),
),
showDateTime
? Center(child: Text(getDateTime()))
: const SizedBox(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton(
onPressed: () {
_selectDateTime(context);
showDateTime = true;
},
child: const Text(' Date & Time '),
),
),
],
),
),
);
}
}
You need to use nullable selectedDate
class _TextScreenState extends State<TextScreen> {
DateTime? selectedDate ;
then make _selectDate nullable return
// Select for Date
Future<DateTime?> _selectDate(BuildContext context) async {
final selected = await showDatePicker(
context: context,
initialDate: selectedDate??DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2025),
);
if (selected != null && selected != selectedDate) {
setState(() {
selectedDate = selected;
});
}
return selectedDate;
}
And while showing _selectTime check selectedDate if it null or not. Also make it null again if you like to avoid cancel second timer loop
// select date time picker
Future<void> _selectDateTime(BuildContext context) async {
final date = await _selectDate(context);
if (date == null) return;
selectedDate = null; // if you want to avoid second loop time picker on cancel
final time = await _selectTime(context);
You also need to add bang on getDate while selectedDate is now nullable. Using bang! because we've check it's null
String getDate() {
// ignore: unnecessary_null_comparison
if (selectedDate == null) {
return 'select date';
} else {
return DateFormat('MMM d, yyyy').format(selectedDate);
}
}
I am adding the table_calendar plugin to my flutter app. Most things are working so far thanks to the help I have been getting from y'all.
Here is the next issue I am getting. When I click on a date on the calendar I get the following error and then nothing happens.
======== Exception caught by gesture ===============================================================
The following _TypeError was thrown while handling a gesture:
type '_MapStream<QuerySnapshot<Map<String, dynamic>>, List<Event>>' is not a subtype of type 'Map<DateTime, List<dynamic>>'
When the exception was thrown, this was the stack:
#0 _AppointmentCalendarScreenState._onDaySelected.<anonymous closure> (package:tonnah/screens/appointment_calendar.dart:73:29)
Here is the code at line 73
void _onDaySelected(DateTime day, List events, List holidays) {
_eventsStream = _firestoreService.getEventStream(day);
setState(() {
_streamController.add(_eventsStream); <<<<< LINE 73
//_selectedEvents = _eventsStream;
});
}
Here is where I populate _eventsStream:
_eventsStream = _firestoreService.getEventStream(_selectedDay);
Stream<List<Event>> getEventStream(DateTime dateTime) {
return _db.collection('agency').doc(globals.agencyId).collection('event')
.where('eventDate', isGreaterThanOrEqualTo: Timestamp.fromDate(dateTime))
.snapshots().map((snapshot) => snapshot.docs
.map((document) => Event.fromFirestore(document.data()))
.toList());
}
It is also used here to put the event indicator on the calendar:
StreamBuilder(
//stream: _firestoreService.getEventStream(_selectedDay),
stream: _db.collection('agency').doc(globals.agencyId).collection('event')
.where('eventDate', isGreaterThanOrEqualTo: Timestamp.fromDate(_selectedDay))
.snapshots().map((snapshot) => snapshot.docs
.map((document) => Event.fromFirestore(document.data()))
.toList()),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Event> snapData;
//return snapData = snapshot.data;
_eventsStream = snapshot.data;
_eventsMap = convertToMap(_eventsStream);
//_selectedEventsMap = _eventsMap[_selectedDay] ?? [];
return _buildTableCalendar();
} else {
return CircularProgressIndicator();
}
},
),
I know there is a type difference but how do I make them match?
Here is the code from the calendar page:
// Example holidays
final Map<DateTime, List> _holidays = {
DateTime(2020, 1, 1): ['New Year\'s Day'],
DateTime(2020, 1, 6): ['Epiphany'],
DateTime(2020, 2, 14): ['Valentine\'s Day'],
DateTime(2020, 4, 21): ['Easter Sunday'],
DateTime(2020, 4, 22): ['Easter Monday'],
};
//final eventsRef = FirebaseFirestore.instance.collection('agency').doc(globals.agencyId).collection('event');
final _firestoreService = FirestoreService();
bool showSpinner = false;
var _eventsStream;
var _eventsMap;
final _selectedDay = DateTime.now();
class AppointmentCalendarScreen extends StatefulWidget {
AppointmentCalendarScreen({Key key, this.title}) : super(key: key);
final String title;
#override
_AppointmentCalendarScreenState createState() => _AppointmentCalendarScreenState();
}
class _AppointmentCalendarScreenState extends State<AppointmentCalendarScreen> with TickerProviderStateMixin {
//var _eventsList;
//Map<DateTime, List> _selectedEventsMap;
AnimationController _animationController;
CalendarController _calendarController;
StreamController<Map<DateTime, List>> _streamController;
#override
void initState() {
super.initState();
_streamController = StreamController();
_eventsStream = _firestoreService.getEventStream(_selectedDay);
_calendarController = CalendarController();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
);
_animationController.forward();
}
#override
void dispose() {
_animationController.dispose();
_calendarController.dispose();
_streamController.close();
super.dispose();
}
void _onDaySelected(DateTime day, List events, List holidays) {
_eventsStream = _firestoreService.getEventStream(day);
setState(() {
_streamController.add(_eventsStream);
//_selectedEvents = _eventsStream;
});
}
void _onVisibleDaysChanged(DateTime first, DateTime last,
CalendarFormat format) {
}
void _onCalendarCreated(DateTime first, DateTime last,
CalendarFormat format) {
}
#override
Widget build(BuildContext context) {
final eventProvider = Provider.of<EventProvider>(context);
FirebaseFirestore _db = FirebaseFirestore.instance;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/Appbar_logo.png',
fit: BoxFit.cover, height: 56),
],
),
),
backgroundColor: Colors.white,
resizeToAvoidBottomInset: false,
body: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
StreamBuilder(
stream: _firestoreService.getEventStream(_selectedDay),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Event> snapData;
_eventsStream = snapshot.data;
_eventsMap = convertToMap(_eventsStream);
//_selectedEventsMap = _eventsMap[_selectedDay] ?? [];
return _buildTableCalendar();
} else {
return CircularProgressIndicator();
}
},
),
const SizedBox(height: 8.0),
//_buildButtons(),
ElevatedButton(
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
globals.newAgency = true;
//eventProvider.saveEvent();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => AddEventScreen()));
setState(() {
showSpinner = false;
});
} catch (e) {
// todo: add better error handling
print(e);
}
},
child: Text('Add Event'),
),
const SizedBox(height: 8.0),
Expanded(child: _buildEventList()),
],
),
);
}
// Simple TableCalendar configuration (using Styles)
Widget _buildTableCalendar() {
return TableCalendar(
calendarController: _calendarController,
locale: 'en_US',
events: _eventsMap,
holidays: _holidays,
startingDayOfWeek: StartingDayOfWeek.sunday,
calendarStyle: CalendarStyle(
selectedColor: Colors.deepOrange[400],
todayColor: Colors.deepOrange[200],
markersColor: Colors.deepPurpleAccent,
outsideDaysVisible: false,
),
headerStyle: HeaderStyle(
formatButtonTextStyle:
TextStyle().copyWith(color: Colors.white, fontSize: 15.0),
formatButtonDecoration: BoxDecoration(
color: Colors.deepOrange[400],
borderRadius: BorderRadius.circular(16.0),
),
),
onDaySelected: _onDaySelected,
onVisibleDaysChanged: _onVisibleDaysChanged,
onCalendarCreated: _onCalendarCreated,
);
}
Widget _buildHolidaysMarker() {
return Icon(
Icons.add_box,
size: 20.0,
color: Colors.blueGrey[800],
);
}
Widget _buildEventList() {
final _db = FirebaseFirestore.instance;
return Container(
child: StreamBuilder<QuerySnapshot>(
//stream: FirestoreService().getEvent(),
stream: _db.collection('agency').doc(globals.agencyId).collection(
'event').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: const Text(
'Loading...',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
));
} else {
var doc = snapshot.data.docs;
return new ListView.builder(
itemCount: doc.length,
itemBuilder: (BuildContext context, int index) {
Event _event = Event.fromFirestore(
doc[index].data());
return ListTile(
isThreeLine: true,
title: Text(
'${_event.eventName ?? 'n/a'}',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.blueAccent),
),
subtitle: Text.rich(TextSpan(
text:
//'${_event.eventName ?? 'n/a'}, '
'${_event.eventStartTime ?? 'n/a'}, '
'Duration: ${_event.eventDuration ?? 'n/a'}',
children: <TextSpan>[
TextSpan(
text:
'\n${_event.eventDescription ?? 'n/a'}',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.blueGrey),
)
])),
//trailing: Text('MLS#: ${_event.mlsNumber ?? 'n/a'}'),
onTap: () {
globals.newTrxn = false;
/*
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
AddEventScreen(
doc[index].data())));
*/
},
);
}
);
}
},
),
);
}
Map<DateTime, List> convertToMap(List<Event> item) {
Map<DateTime, List> result;
for (int i = 0; i < item.length; i++) {
Event data = item[i];
//get the date and convert it to a DateTime variable
//DateTime currentDate = DateFormat('MM-dd-yyyy - kk:mm').format(data.eventDate);
DateTime currentDate = DateTime(data.eventDate.year, data.eventDate.month, data.eventDate.day, data.eventDate.hour, data.eventDate.minute);
List eventNames = [];
//add the event name to the the eventNames list for the current date.
//search for another event with the same date and populate the eventNames List.
for (int j = 0; j < item.length; j++) {
//create temp calendarItemData object.
Event temp = item[j];
//establish that the temp date is equal to the current date
if (data.eventDate == temp.eventDate) {
//add the event name to the event List.
eventNames.add(temp.eventName);
} //else continue
}
//add the date and the event to the map if the date is not contained in the map
if (result == null) {
result = {
currentDate: eventNames
};
} else {
result[currentDate] = eventNames;
}
return result;
}
}
}
You have to convert on type: Map<DateTime, List>.
Create method which will convert '_MapStream<QuerySnapshot<Map<String, dynamic>>, List>' on 'Map<DateTime, List>'
In your case: convert on 'Map<DateTime, List< "here you should add all data which you want to have in calendar view. Can be map or sth else">>
Actually, you need thre methods:
First: Convert method which I mentioned above. Method exemple below
final int _currentYearInt = DateTime.now().year;
final int _currentMonthInt =
int.parse(addZeroToNumberLowerThanTen(number: DateTime.now().month));
documentsList.forEach((doc) {
Map<DateTime, List<Shift>> _schedule = {};
// "days" is a properties with values: {"10" : [], "12": []}, where "10" is day of month"
doc['days'].forEach((k, v) {
int _shiftDay = int.parse(k);
if (calendarWithMarkedDates[
DateTime.utc(_currentYearInt, _currentMonthInt, _shiftDay)] ==
null) {
calendarWithMarkedDates[
DateTime.utc(_currentYearInt, _currentMonthInt, _shiftDay)] = [];
}
calendarWithMarkedDates[DateTime.utc(_currentYearInt, _currentMonthInt, _shiftDay)]!
.add( "your item. Can be object, map or what you want" );
});
});
Second: "calendarWithMarkedDates" is a LinkedHashMap, which holds all the marked dates. F.e. from server has response dates: 1.07, 5.07, 12.07. All dates after convert to LinkedHashMap are storing in this "calendarWithMarkedDates".
final calendarWithMarkedDates = LinkedHashMap<DateTime, List<Shift>>(
equals: isSameDay,
hashCode: getHashCode,
);
Third: "currentScheduleForDay" it is function which yu hava to invoke like a value of property "eventLoader" in TableCalendar without parameters.
It means:
good way currentScheduleForDay
bad way: currentScheduleForDay()
List<Shift> currentScheduleForDay(DateTime day) {
return currentCalendarWithMarkedDates[day] ?? [];
}
When use method currentScheduleForDay (without "()"), TableCalendar by it self made loops through all dates in month and inovke method for all day.
I want to pick a date from the calendarIcon(which is implemented with the showDatePicker()) inside a textField widget and feed it to the textField. But it is not working. And I want to manage the UI states using Provider. I Am showing my code below. Am I doing it wrong? or Is there another way to do it.
I tried doing the following way:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SignUp extends StatelessWidget {
Widget build(BuildContext context) {
final validator = Provider.of<SignUpController>(context);
return Scaffold(
appBar: AppBar(
title: Text("Demo FormValidation Provider"),
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: ListView(children: <Widget>[
TextField(
controller: TextEditingController()..text = "Enter DOB",
onChanged: (text) => {validator.date},
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.calendar_today_sharp),
onPressed: () => {validator.showCalender(context)},
),
),
)
])),
);
}
}
class SignUpController with ChangeNotifier {
Future<DateTime> showCalender(BuildContext context) {
notifyListeners();
return showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
);
}
var date = DateTime.now().toString().split(' ')[0];
void getDate(BuildContext context) async {
var dateL = await showCalender(context);
date = (dateL.toLocal()).toString().split(' ')[0];
notifyListeners();
}
}
define a TextEditingController then pass data to it like this:
//define
TextEditingController text = TextEditingController(text:'');
//pass data
text = TextEditingController(text:data);
first, make ur widget stateful then add didChangeDependebcies() method it will listen.
void didChangeDependencies() {
super.didChangeDependencies();
setState(() {
getImages();
});
}
Future getImages() async {
//get ur data here from provider
}
SOLVED!!!
Finally did it..
Reference: For more information refer to this medium.com article by Pinkesh Darji
Solution
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// UI class
class SignUp extends StatelessWidget {
Widget build(BuildContext context) {
final validator = Provider.of<SignUpController>(context);
return Scaffold(
appBar: AppBar(
title: Text("Demo FormValidation Provider"),
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
TextField(
controller: TextEditingController(
text: validator.selectedDate == null
? "Enter Date Of Birth"
: "${validator.selectedDate}".split(' ')[0]),
onChanged: (String value) {
validator.changeDOB(value);
},
decoration: InputDecoration(
labelText: "yyyy-mm-dd",
errorText: validator.dob.error,
suffixIcon: IconButton(
icon: Icon(Icons.calendar_today_sharp),
onPressed: () => {validator.selectDate(context)},
),
),
),
SizedBox(
height: 10,
),
ElevatedButton(
// The ElevatedButton inherits the color of the app.
child: Text("Submit"),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(10),
textStyle: TextStyle(
fontSize: 20,
),
),
onPressed: () {
print("Date: " + "${validator.selectedDate}".split(' ')[0]);
},
),
],
),
),
);
}
}
// Controller Class
class SignUpController with ChangeNotifier {
ValidationItem _dob = ValidationItem(null, null);
// Getters
ValidationItem get dob => _dob;
// making the showDatePicker method
DateTime selectedDate;
selectDate(BuildContext context) async {
DateTime picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
// initialDatePickerMode: DatePickerMode.year,
);
if (picked != null && picked != selectedDate) {
selectedDate = picked;
}
notifyListeners();
}
// Setters
void changeDOB(String value) {
try {
// converting the DateTime data type into a String data type and only getting the
// Date(discarding the time)
value = selectedDate.toString().split(' ')[0];
_dob = ValidationItem(value, null);
} catch (error) {
_dob = ValidationItem(null, "Use Correct Format");
}
notifyListeners();
}
}
// Model Class
class ValidationItem {
final String value;
final String error;
ValidationItem(this.value, this.error);
}
NOTE01: You can use multiple consumers for the provider (or maybe not??)
NOTE02: Above answer will not make the initial text(TextEditingController) editable. It can be achieved using the EditableText widget or maybe some other functions. Try it.
I am trying to implement a time picker in my flutter which display time like this see picture below.
although I am able to format time like this but problem is it does not display the current time when the widget is initialized.
I tried using this. here see my code.
TimeOfDay _currentTime = new TimeOfDay.now();
String timeText = 'Set A Time';
Future<Null> selectTime(BuildContext context) async {
TimeOfDay selectedTime = await showTimePicker(
context: context,
initialTime: _currentTime,
);
MaterialLocalizations localizations = MaterialLocalizations.of(context);
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
if (formattedTime != null) {
setState(() {
timeText = formattedTime;
});
}
};
Widget Date picker
class DatePicker extends StatelessWidget {
DatePicker({
this.formatedDate,
this.selectedDate,
});
final String formatedDate;
final Function selectedDate;
#override
Widget build(BuildContext context) {
return FlatButton(
padding: EdgeInsets.only(top: 30.0, bottom: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
Icons.calendar_today,
color: Color(kBorderTileColor),
),
SizedBox(
width: 50.0,
),
Text(
'$formatedDate',
style: kInputTextStyle,
),
SizedBox(
width: 50.0,
),
Text(
'All Day',
style: TextStyle(color: Color(kBorderTileColor)),
),
],
),
onPressed: () {
selectedDate(context);
},
);
}
}
Try this source code.
TimeOfDay _currentTime = new TimeOfDay.now();
String timeText = 'Set A Time';
Future<Null> selectTime(BuildContext context) async {
TimeOfDay selectedTime = await showTimePicker(
context: context,
initialTime: _currentTime,
);
MaterialLocalizations localizations = MaterialLocalizations.of(context);
if (selectedTime != null) {
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
if (formattedTime != null) {
setState(() {
timeText = formattedTime;
});
}
}
}
void setCurrentTime(){
TimeOfDay selectedTime=new TimeOfDay.now();
MaterialLocalizations localizations = MaterialLocalizations.of(context);
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
if (formattedTime != null) {
setState(() {
timeText = formattedTime;
});
}
}
Call this function in build(BuildContext context)
Widget build(BuildContext context) {
setCurrentTime();
...
}
I am working on a simple Todolist app. I have created a database to store the data under the file "to_do_item.dart". the file "database_helper.dart" is where i have stored all the CRUD functions.
When the app is opened, the user will see a screen created under the file "todo_list.dart" where the added tasks will be displayed in a listview. when the user clicks the action button, it will send them to "input_screen.dart" where there is a textfield to enter the tasks. after the user clicks "done" on the on-screen keyboard, the data will be saved in a list called "Itemlist" (line 49 of "input_screen").
I want to send this list to "todo_list.dart" and display them in a scrollable listview but i am unsure of what method to use. At the moment i am only trying to add the item and display it.
Tried using getters but was unable to use it for another class, and tried searching how other todolist apps on flutter were made but they use an AlertDialog on the same class; i want to take input from one screen(class InputScreen) and display it on another screen (class TodoList).
CODE UNDER "todo_list.dart" -
Container(
color: Colors.white,
alignment: Alignment.topLeft,
margin: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 110,),
Text( "tasks for today",),
SizedBox(height: 20,),
//Todo: display listview on below column
// Column(
// children: <Widget>[
// new Flexible(child: ListView.builder(
itemBuilder:(_,intindex){
// return ListTile(
//// title: _itema,
// );
// }
// )
// )
// ],
// ),
SizedBox(height: 320,),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => InputScreen()));
},
heroTag: "btn2",
child: Icon(Icons.add, color: Colors.white,), backgroundColor: Colors.black87,),
],
)
]
),
),
CODE UNDER "input_screen.dart"
class InputScreen extends StatefulWidget {
#override
_InputScreenState createState() => _InputScreenState();
}
class _InputScreenState extends State<InputScreen> {
#override
void initState() {
// TODO: implement initState
super.initState();
_readList();
}
void _handleSubmitted(String text) async{
taskcontroller.clear();
TodoItem obj = new TodoItem(text, DateTime.now().toIso8601String());
int SavedItemId = await db.saveItem(obj);
print("item saved id: $SavedItemId");
print("$text");
TodoItem addedItem = await db.getItem(SavedItemId);
setState(() {
Itemlist.insert(0, addedItem);
});
}
_readList() async{
List items = await db.getItems();
items.forEach((item) {
TodoItem todoItem = TodoItem.map(items);
print("db it4ems: ${todoItem.itemName.toString()}");
});
}
var db = new DatabaseHelper();
final List<TodoItem> Itemlist= <TodoItem>[];
TextEditingController taskcontroller = new TextEditingController();
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.white,
alignment: Alignment.topLeft,
margin: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 110,),
Text("tasks for today:",),
SizedBox(height: 10,),
//Todo: remove sized box below and display tasks there
TextField(
autofocus: true,
onEditingComplete: (){Navigator.pop(context);},
maxLength: 10,
onSubmitted: (NewValue){ _handleSubmitted(NewValue);},
controller: taskcontroller,
decoration: InputDecoration(
labelText: "enter tasks here"
),
style: TextStyle(height: 1.2, fontSize: 20, color: Colors.black87),
)
]
),
),
],
),
),
);
}
}
CODE UNDER "to_do_item.dart" -
import 'package:flutter/material.dart';
class TodoItem extends StatelessWidget {
String _itemName;
String _dateCreated;
int _id;
TodoItem(this._itemName, this._dateCreated);
TodoItem.map(dynamic obj) {
this._itemName = obj["itemName"];
this._dateCreated = obj["dateCreated"];
this._id = obj["id"];
}
String get itemName => _itemName;
String get dateCreated => _dateCreated;
int get id => _id;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
map["itemName"] = _itemName;
map["dateCreated"] = _dateCreated;
if (_id != null) {
map["id"] = _id;
}
return map;
}
TodoItem.fromMap(Map<String, dynamic> map) {
this._itemName = map["itemName"];
this._dateCreated = map["dateCreated"];
this._id = map["id"];
}
#override
Widget build(BuildContext context) {
return new Container(
margin: const EdgeInsets.all(8.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(_itemName, )
],
),
],
),
);
}
}
CODE UNDER "database_helper.dart" -
import 'dart:io';
import 'to_do_item.dart';
import 'package:path/path.dart';
import 'dart:async';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
final String tableName = "todotbl";
final String columnId = "id";
final String columnItemName = "itemName";
final String columnDateCreated = "dateCreated";
static Database _db;
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
DatabaseHelper.internal();
initDb() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, "todolist_db.db");
var ourDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return ourDb;
}
void _onCreate(Database db, int version) async {
await db.execute(
"CREATE TABLE $tableName(id INTEGER PRIMARY KEY, $columnItemName TEXT, $columnDateCreated TEXT)");
print("Table is created");
}
//insertion
Future<int> saveItem(TodoItem item) async {
var dbClient = await db;
int res = await dbClient.insert("$tableName", item.toMap());
print(res.toString());
return res;
}
//Get
Future<List> getItems() async {
var dbClient = await db;
var result = await dbClient.rawQuery(
"SELECT * FROM $tableName ORDER BY $columnItemName ASC"); //ASC
return result.toList();
// if (result.length == 0) return [];
// var users = [];
//
// for (Map<String, dynamic> map in result) {
// users.add(new User.fromMap(map));
// }
//
// return users;
}
Future<int> getCount() async {
var dbClient = await db;
return Sqflite.firstIntValue(await dbClient.rawQuery(
"SELECT COUNT(*) FROM $tableName"
));
}
//
Future<TodoItem> getItem(int id) async {
var dbClient = await db;
var result = await dbClient.rawQuery(
"SELECT * FROM $tableName WHERE id = $id");
if (result.length == 0) return null;
return new TodoItem.fromMap(result.first);
}
//deletion
// Future<int> deleteItem(int id) async {
// var dbClient = await db;
// var result = await dbClient.rawQuery("DELETE FROM $tableName WHERE id = $id");
// if (result.length == 0) return null;
// return result.first as int;
// }
Future<int> deleteItem(int id) async {
var dbClient = await db;
return await dbClient.delete(tableName,
where: "$columnId = ?", whereArgs: [id]);
}
Future<int> updateItem(TodoItem item) async {
var dbClient = await db;
return await dbClient.update("$tableName", item.toMap(),
where: "$columnId = ?", whereArgs: [item.id]);
}
Future close() async {
var dbClient = await db;
return dbClient.close();
}
}
This is possibly a duplicate of this
Nonetheless I hope this helps
class FrontPage extends StatefulWidget {
#override
_FrontPageState createState() => _FrontPageState();
}
class _FrontPageState extends State<FrontPage> {
// Activity currentActivity;
var recentActivities = <Widget>[];//<Widget>[Activity("Skiing", "assets/activity_logos/Squat.png").getSummary(),Activity("Skiing", "assets/activity_logos/Squat.png").getSummary(),Activity("Skiing", "assets/activity_logos/Squat.png").getSummary(),Activity("Skiing", "assets/activity_logos/Squat.png").getSummary(),Activity("Skiing", "assets/activity_logos/Squat.png").getSummary(),Activity("Skiing", "assets/activity_logos/Squat.png").getSummary()];
var upcomingActivities = <Widget>[];//<Widget>[Activity("Jog", "assets/activity_logos/Squat.png", status: ActivityStatus.completed,).getSummary()];
List<String> tasks = ["previous1", "previous2"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Home'),
actions: <Widget>[
GestureDetector(
child: Align(child:Text("+", style: TextStyle(fontSize: 40)), alignment: Alignment.centerLeft, widthFactor: 2,),
onTap: () => Navigator.pushNamed(context, '/activity/new'),
)
],
),
body: Container(
color: Colors.white,
alignment: Alignment.topLeft,
margin: const EdgeInsets.all(20),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 110,),
Text( "tasks for today",),
SizedBox(height: 20,),
Expanded(child: ListView.builder(
itemCount: tasks.length,
itemBuilder: (BuildContext context, int index) {
return Text(tasks[index]);
})),
]),
FloatingActionButton(
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InputScreen(),
));
setState(() {
tasks.add(result);
});
},
heroTag: "btn2",
child: Icon(Icons.add, color: Colors.white,), backgroundColor: Colors.black87,),
]),
),
);
}
}
class InputScreen extends StatefulWidget {
#override
_InputScreenState createState() => _InputScreenState();
}
class _InputScreenState extends State<InputScreen> {
#override
void initState() {
// TODO: implement initState
super.initState();
// _readList();
}
// final List<TodoItem> Itemlist= <TodoItem>[];
TextEditingController taskcontroller = new TextEditingController();
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.white,
alignment: Alignment.topLeft,
margin: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 110,),
Text("tasks for today:",),
SizedBox(height: 10,),
//Todo: remove sized box below and display tasks there
TextField(
autofocus: true,
onEditingComplete: () {
String textToSendBack = taskcontroller.text;
Navigator.pop(context, textToSendBack );
},
maxLength: 10,
onSubmitted: (NewValue){ },
controller: taskcontroller,
decoration: InputDecoration(
labelText: "enter tasks here"
),
style: TextStyle(height: 1.2, fontSize: 20, color: Colors.black87),
)
]
),
),
],
),
),
);
}
}
class TodoItem extends StatelessWidget {
String _itemName;
String _dateCreated;
int _id;
TodoItem(this._itemName, this._dateCreated);
TodoItem.map(dynamic obj) {
this._itemName = obj["itemName"];
this._dateCreated = obj["dateCreated"];
this._id = obj["id"];
}
String get itemName => _itemName;
String get dateCreated => _dateCreated;
int get id => _id;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
map["itemName"] = _itemName;
map["dateCreated"] = _dateCreated;
if (_id != null) {
map["id"] = _id;
}
return map;
}
TodoItem.fromMap(Map<String, dynamic> map) {
this._itemName = map["itemName"];
this._dateCreated = map["dateCreated"];
this._id = map["id"];
}
#override
Widget build(BuildContext context) {
return new Container(
margin: const EdgeInsets.all(8.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(_itemName, )
],
),
],
),
);
}
}
The main changes consist of
On input page:
TextField(
autofocus: true,
onEditingComplete: () {
String textToSendBack = taskcontroller.text;
Navigator.pop(context, textToSendBack );
}
On the front page:
FloatingActionButton(
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InputScreen(),
));
setState(() {
tasks.add(result);
});
},
Some widgeting layout, and I am keeping the data in a tasks array but you can change as you wish.