Flutter: To-do List App (Part 4) - PageView Navigation


Project Structure



Source Code

lib/widgets/custom_modal_action_button.dart
import 'package:flutter/material.dart';
import 'package:todoapp/widgets/custom_button.dart';
class CustomModalActionButton extends StatelessWidget {
final VoidCallback onClose;
final VoidCallback onSave;
CustomModalActionButton({@required this.onClose, @required this.onSave});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomButton(
onPressed: onClose,
buttonText: "Close",
),
CustomButton(
onPressed: onSave,
buttonText: "Save",
color: Theme.of(context).accentColor,
textColor: Colors.white,
)
],
);
}
}

lib/widgets/custom_icon_decoration.dart
import 'package:flutter/material.dart';
class CustomIconDecoration extends Decoration {
final double iconSize;
final double lineWidth;
final bool firstData;
final bool lastData;
CustomIconDecoration({
@required double iconSize,
@required double lineWidth,
@required bool firstData,
@required bool lastData,
}) : this.iconSize = iconSize,
this.lineWidth = lineWidth,
this.firstData = firstData,
this.lastData = lastData;
@override
BoxPainter createBoxPainter([onChanged]) {
return IconLine(
iconSize: iconSize,
lineWidth: lineWidth,
firstData: firstData,
lastData: lastData);
}
}
class IconLine extends BoxPainter {
final double iconSize;
final bool firstData;
final bool lastData;
final Paint paintLine;
IconLine({
@required double iconSize,
@required double lineWidth,
@required bool firstData,
@required bool lastData,
}) : this.iconSize = iconSize,
this.firstData = firstData,
this.lastData = lastData,
paintLine = Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..strokeWidth = lineWidth
..style = PaintingStyle.stroke;
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final leftOffset = Offset((iconSize / 2) + 24, offset.dy);
final double iconSpace = iconSize / 1.5;
final Offset top = configuration.size.topLeft(Offset(leftOffset.dx, 0.0));
final Offset centerTop = configuration.size
.centerLeft(Offset(leftOffset.dx, leftOffset.dy - iconSpace));
final Offset centerBottom = configuration.size
.centerLeft(Offset(leftOffset.dx, leftOffset.dy + iconSpace));
final Offset end =
configuration.size.bottomLeft(Offset(leftOffset.dx, leftOffset.dy * 2));
if (!firstData) canvas.drawLine(top, centerTop, paintLine);
if (!lastData) canvas.drawLine(centerBottom, end, paintLine);
}
}

lib/widgets/custom_date_time_picker.dart
import 'package:flutter/material.dart';
class CustomDateTimePicker extends StatelessWidget {
final VoidCallback onPressed;
final IconData icon;
final String value;
CustomDateTimePicker(
{@required this.onPressed, @required this.icon, @required this.value});
@override
Widget build(BuildContext context) {
return FlatButton(
padding: EdgeInsets.zero,
onPressed: onPressed,
child: Padding(
padding: const EdgeInsets.only(left: 12.0),
child: Row(
children: <Widget>[
Icon(icon, color: Theme.of(context).accentColor, size: 30),
SizedBox(
width: 12,
),
Text(
value,
style: TextStyle(fontSize: 14),
)
],
),
),
);
}
}

lib/pages/add_task_page.dart
import 'package:flutter/material.dart';
import 'package:todoapp/widgets/custom_date_time_picker.dart';
import 'package:todoapp/widgets/custom_modal_action_button.dart';
import 'package:todoapp/widgets/custom_textfield.dart';
class AddTaskPage extends StatefulWidget {
@override
_AddTaskPageState createState() => _AddTaskPageState();
}
class _AddTaskPageState extends State<AddTaskPage> {
String _selectedDate = 'Pick date';
String _selectedTime = 'Pick time';
Future _pickDate() async {
DateTime datepick = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime.now().add(Duration(days: -365)),
lastDate: new DateTime.now().add(Duration(days: 365)));
if (datepick != null)
setState(() {
_selectedDate = datepick.toString();
});
}
Future _pickTime() async {
TimeOfDay timepick = await showTimePicker(
context: context, initialTime: new TimeOfDay.now());
if (timepick != null) {
setState(() {
_selectedTime = timepick.toString();
});
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Text(
"Add new task",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
)),
SizedBox(
height: 24,
),
CustomTextField(labelText: 'Enter task name'),
SizedBox(height: 12),
CustomDateTimePicker(
icon: Icons.date_range,
onPressed: _pickDate,
value: _selectedDate,
),
CustomDateTimePicker(
icon: Icons.access_time,
onPressed: _pickTime,
value: _selectedTime,
),
SizedBox(
height: 24,
),
CustomModalActionButton(
onClose: () {
Navigator.of(context).pop();
},
onSave: () {},
)
],
),
);
}
}

lib/pages/add_event_page.dart
import 'package:flutter/material.dart';
import 'package:todoapp/widgets/custom_date_time_picker.dart';
import 'package:todoapp/widgets/custom_modal_action_button.dart';
import 'package:todoapp/widgets/custom_textfield.dart';
class AddEventPage extends StatefulWidget {
@override
_AddEventPageState createState() => _AddEventPageState();
}
class _AddEventPageState extends State<AddEventPage> {
String _selectedDate = 'Pick date';
String _selectedTime = 'Pick time';
Future _pickDate() async {
DateTime datepick = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime.now().add(Duration(days: -365)),
lastDate: new DateTime.now().add(Duration(days: 365)));
if (datepick != null)
setState(() {
_selectedDate = datepick.toString();
});
}
Future _pickTime() async {
TimeOfDay timepick = await showTimePicker(
context: context, initialTime: new TimeOfDay.now());
if (timepick != null) {
setState(() {
_selectedTime = timepick.toString();
});
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Text(
"Add new event",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
)),
SizedBox(
height: 24,
),
CustomTextField(labelText: 'Enter event name'),
SizedBox(height: 12),
CustomTextField(labelText: 'Enter description'),
SizedBox(height: 12),
CustomDateTimePicker(
icon: Icons.date_range,
onPressed: _pickDate,
value: _selectedDate,
),
CustomDateTimePicker(
icon: Icons.access_time,
onPressed: _pickTime,
value: _selectedTime,
),
SizedBox(
height: 24,
),
CustomModalActionButton(
onClose: () {
Navigator.of(context).pop();
},
onSave: () {},
)
],
),
);
}
}

lib/pages/event_page.dart
import 'package:flutter/material.dart';
import 'package:todoapp/widgets/custom_icon_decoration.dart';
class EventPage extends StatefulWidget {
@override
_EventPageState createState() => _EventPageState();
}
class Event {
final String time;
final String task;
final String desc;
final bool isFinish;
const Event(this.time, this.task, this.desc, this.isFinish);
}
final List<Event> _eventList = [
new Event("08:00", "Have coffe with Sam", "Personal", true),
new Event("10:00", "Meet with sales", "Work", true),
new Event("12:00", "Call Tom about appointment", "Work", false),
new Event("14:00", "Fix onboarding experience", "Work", false),
new Event("16:00", "Edit API documentation", "Personal", false),
new Event("18:00", "Setup user focus group", "Personal", false),
];
class _EventPageState extends State<EventPage> {
@override
Widget build(BuildContext context) {
double iconSize = 20;
return ListView.builder(
itemCount: _eventList.length,
padding: const EdgeInsets.all(0),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(left: 24.0, right: 24),
child: Row(
children: <Widget>[
_lineStyle(context, iconSize, index, _eventList.length,
_eventList[index].isFinish),
_displayTime(_eventList[index].time),
_displayContent(_eventList[index])
],
),
);
},
);
}
Widget _displayContent(Event event) {
return Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 12.0, bottom: 12.0),
child: Container(
padding: const EdgeInsets.all(14.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Color(0x20000000),
blurRadius: 5,
offset: Offset(0, 3))
]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(event.task),
SizedBox(
height: 12,
),
Text(event.desc)
],
),
),
),
);
}
Widget _displayTime(String time) {
return Container(
width: 80,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(time),
));
}
Widget _lineStyle(BuildContext context, double iconSize, int index,
int listLength, bool isFinish) {
return Container(
decoration: CustomIconDecoration(
iconSize: iconSize,
lineWidth: 1,
firstData: index == 0 ?? true,
lastData: index == listLength - 1 ?? true),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50)),
boxShadow: [
BoxShadow(
offset: Offset(0, 3),
color: Color(0x20000000),
blurRadius: 5)
]),
child: Icon(
isFinish
? Icons.fiber_manual_record
: Icons.radio_button_unchecked,
size: iconSize,
color: Theme.of(context).accentColor),
));
}
}

lib/main.dart
import 'package:flutter/material.dart';
import 'package:todoapp/pages/add_event_page.dart';
import 'package:todoapp/pages/add_task_page.dart';
import 'package:todoapp/pages/event_page.dart';
import 'package:todoapp/pages/task_page.dart';
import 'package:todoapp/widgets/custom_button.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.red, fontFamily: "Montserrat"),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
PageController _pageController = PageController();
double currentPage = 0;
@override
Widget build(BuildContext context) {
_pageController.addListener(() {
setState(() {
currentPage = _pageController.page;
});
});
return Scaffold(
body: Stack(
children: <Widget>[
Container(
height: 35,
color: Theme.of(context).accentColor,
),
Positioned(
right: 0,
child: Text(
"6",
style: TextStyle(fontSize: 200, color: Color(0x10000000)),
),
),
_mainContent(context),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return Dialog(
child: currentPage == 0 ? AddTaskPage() : AddEventPage(),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))));
});
},
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
)
],
),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _mainContent(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 60),
Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
"Monday",
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: _button(context),
),
Expanded(
child: PageView(
controller: _pageController,
children: <Widget>[TaskPage(), EventPage()],
))
],
);
}
Widget _button(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: CustomButton(
onPressed: () {
_pageController.previousPage(
duration: Duration(milliseconds: 500),
curve: Curves.bounceInOut);
},
buttonText: "Tasks",
color:
currentPage < 0.5 ? Theme.of(context).accentColor : Colors.white,
textColor:
currentPage < 0.5 ? Colors.white : Theme.of(context).accentColor,
borderColor: currentPage < 0.5
? Colors.transparent
: Theme.of(context).accentColor,
)),
SizedBox(
width: 32,
),
Expanded(
child: CustomButton(
onPressed: () {
_pageController.nextPage(
duration: Duration(milliseconds: 500),
curve: Curves.bounceInOut);
},
buttonText: "Events",
color:
currentPage > 0.5 ? Theme.of(context).accentColor : Colors.white,
textColor:
currentPage > 0.5 ? Colors.white : Theme.of(context).accentColor,
borderColor: currentPage > 0.5
? Colors.transparent
: Theme.of(context).accentColor,
))
],
);
}
}
view raw main_4.dart hosted with ❤ by GitHub

Comments

Popular posts from this blog