Open In App

Flutter – Create Interactive Event Calendars

This article describes how to integrate event management functionality into a Flutter project. We will focus on adding and displaying events in a calendar interface. We will use the GetX package for routing and other utilities, but other solutions are also possible. We will use the get_cli tool to create a well-structured Flutter project. This tool helps us to generate complex project hierarchies, such as views, controllers, and pages.

The main focus of this article is on dynamically rendering events in the Flutter calendar. We will implement a demonstration program that displays events and allows users to create new events directly in the calendar interface. To do this, we will use the table_calendar package, which integrates seamlessly with Flutter and provides a comprehensive solution for managing events in a calendar environment.



Event Calander App

This is what we will be building in this article:

Use Case Or Why Integrate Event Calander In a Flutter App

There are many use cases for integrating an event calendar in a Flutter app. One of the most common is to allow users to view and manage their upcoming events, such as appointments, meetings, and social engagements. This can be helpful for users who need to stay organized and on top of their schedules. Another common use case is to display events that are relevant to the app’s content or purpose. For example, a news app could use an event calendar to display upcoming events in the local area, or a social media app could use an event calendar to display upcoming events that users are interested in.



Integrating an event calendar in a Flutter app can also be used to create new and innovative features. For example, a fitness app could use an event calendar to track users’ workouts and fitness goals. Or, a travel app could use an event calendar to display upcoming flights and hotel reservations.

What are the benefits of using the GetX package and the get_cli tool?

Benefits of using the GetX package:

Benefits of using the get_cli tool:

What is the table_calendar package?

The table_calendar package is a Flutter package that provides a highly customizable, feature-packed calendar widget. It is easy to use and provides a preconfigured UI with customizable styling. It also supports custom selective builders for unlimited UI design, locale support, range selection support, multiple selection support, dynamic events and holidays, vertical autosizing, and multiple calendar formats (month, two weeks, week).

In simple words, the table_calendar package is a great way to add a calendar to your Flutter app. It is easy to use and provides a lot of features and customization options.

Getting Started

Install get_cli globally:

To install get_cli globally we need to run this command from the root:

pub global activate get_cli

or

flutter pub global activate get_cli

If you are running one of these commands successfuly then you have get_cli installed on your system and now we can start using it.

For more detailed instruction search get_cli on pub.dev.

Create Flutter Project

Create a flutter project, give it a name of your choice. And once done, open the root of the project in a terminal, and run the following command to start creating folder structure as per get_cli.

get init

Choose the default opetions like kotline, swift and else. Once you come to choose architecure you will choose Getx Cli and not clean architecutre.

Once done your project structure would look like this, but you won’t be having create_event and detail_screen directory as of now.

Those will be created once you run the following command:

 get create page:create_event

and

get create page:detail_screen

Project Structure:

Below is the entire project structure you would have by now.


├── app
│ ├── data
│ ├── modules
│ │ └── home
│ │ ├── bindings
│ │ │ └── home_binding.dart
│ │ ├── controllers
│ │ │ └── home_controller.dart
│ │ ├── create_event
│ │ │ ├── bindings
│ │ │ │ └── create_event_binding.dart
│ │ │ ├── controllers
│ │ │ │ └── create_event_controller.dart
│ │ │ └── views
│ │ │ └── create_event_view.dart
│ │ ├── detail_screen
│ │ │ ├── bindings
│ │ │ │ └── detail_screen_binding.dart
│ │ │ ├── controllers
│ │ │ │ └── detail_screen_controller.dart
│ │ │ └── views
│ │ │ └── detail_screen_view.dart
│ │ └── views
│ │ └── home_view.dart
│ └── routes
│ ├── app_pages.dart
│ └── app_routes.dart
├── main.dart
└── static
└── utils.dart


Now we are going to start the actual coding part.

main.dart




import 'package:flutter/material.dart';
  
import 'package:get/get.dart';
  
import 'app/routes/app_pages.dart';
  
void main() {
  runApp(
    GetMaterialApp(
      title: "Application",
      initialRoute: AppPages.INITIAL,
      getPages: AppPages.routes,
      theme: ThemeData(
          // Darrk theme
          brightness: Brightness.dark,
          primarySwatch: Colors.grey),
    ),
  );
}

In this main.dart code above, a Flutter application is being initialized using the runApp method. The application is built using the GetX library, as indicated by the GetMaterialApp widget. Key attributes are set, such as the app’s title, initial route, navigation routes, and theme configuration. Notably, the theme is defined as a dark theme with a grey primary swatch. This code serves as the entry point for the Flutter application, defining its structure and initial configuration for navigation and theming.

Now lets see how we have initialied the routes in this app.

app_pages.dart




import 'package:get/get.dart';
  
import '../modules/home/create_event/bindings/create_event_binding.dart';
import '../modules/home/create_event/views/create_event_view.dart';
import '../modules/home/detail_screen/bindings/detail_screen_binding.dart';
import '../modules/home/detail_screen/views/detail_screen_view.dart';
import '../modules/home/bindings/home_binding.dart';
import '../modules/home/views/home_view.dart';
  
part 'app_routes.dart';
  
// class AppPages 
class AppPages {
  AppPages._();
  
  static const INITIAL = Routes.HOME;
  
  static final routes = [
    // getPages to create a list of GetPage to be 
    // used in the GetMaterialApp.router property.
    GetPage(
      name: _Paths.HOME,
      page: () => const CustomTableCalendar(),
      binding: HomeBinding(),
    ),
    GetPage(
      name: _Paths.CREATE_EVENT,
      page: () => SecondScreen(),
      binding: CreateEventBinding(),
    ),
  ];
}

In this file we have defined routing configurations for a Flutter application using the GetX library. It starts by importing necessary modules and widgets. The code also imports specific bindings and views for different screens, like the home screen, create event screen, and detail screen, ensuring that data dependencies are resolved correctly when these screens are accessed.

The AppPages class centralizes routing information, specifying the initial route as the home screen. The routes list contains definitions for various routes, associating each route name with a corresponding view and binding. For instance, the ‘HOME‘ route is linked to the CustomTableCalendar view and the HomeBinding binding, while the ‘CREATE_EVENT‘ route corresponds to the SecondScreen view and the CreateEventBinding binding. This code helps manage navigation and data binding within the Flutter application efficiently.

home_binding.dart




import 'package:get/get.dart';
  
import '../controllers/home_controller.dart';
  
// this class is used to inject dependencies in your controller
class HomeBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut<HomeController>(
      () => HomeController(),
    );
  }
}

This Dart code, used in a Flutter app with GetX, manages and injects dependencies into the HomeController class. It extends the Bindings class, indicating that an instance of HomeController is lazily created and associated with GetX, ensuring efficient dependency management in the app. This enhances organization and efficiency in handling dependencies within the Flutter application.

One good thing about git_cli is that we don’t need to worry about the creation of these above boiler plate code for routing and binding.

Before getting on to the home page, let’s first make a class which will contain the colors used in our app.




import 'package:flutter/material.dart';
  
// The AppColors class stores color constants used throughout the application.
class AppColors {
  AppColors._(); // Private constructor to prevent instantiation.
  
  // Define color constants as static members of the class.
  static const Color blackCoffee = Color(0xFF352d39); // Dark brownish-black color.
  static const Color eggPlant = Color(0xFF6d435a); // Eggplant purple color.
  static const Color celeste = Color(0xFFb1ede8); // Light blue-green color.
  static const Color babyPowder = Color(0xFFFFFcF9); // Soft off-white color.
  static const Color ultraRed = Color(0xFFFF6978); // Bright red color.
}

In this code, the AppColors class serves as a convenient way to access and reuse color values throughout your Flutter application. These color constants are defined as static const members of the class, making it easy to reference them in different parts of your code to ensure consistent and maintainable color theming.

Now we will start coding the home page of our app.

home_view.dart




// Scaffold widget is used to create the app's basic structure.
Scaffold(
  appBar: AppBar(
    title: const Text('Schedule'), // Set the app's title.
    // Add a hamburger menu icon to the leading position in the app bar.
    leading: Builder(
      builder: (context) => IconButton(
        icon: const Icon(Icons.menu),
        onPressed: () => Scaffold.of(context).openDrawer(),
      ),
    ),
    // Add a notification icon with a red circle in the app bar's actions.
    actions: [
      Stack(
        children: [
          IconButton(
            onPressed: () {},
            icon: const Icon(Icons.notifications),
          ),
          Positioned(
            right: 11,
            top: 11,
            child: Container(
              padding: const EdgeInsets.all(1),
              decoration: BoxDecoration(
                color: AppColors.ultraRed, // Red circle color.
                borderRadius: BorderRadius.circular(6),
              ),
              constraints: const BoxConstraints(
                minWidth: 14,
                minHeight: 14,
              ),
              child: const Text(
                '3', // Display the notification count.
                style: TextStyle(
                  color: AppColors.babyPowder, // Text color.
                  fontSize: 8,
                ),
                textAlign: TextAlign.center,
              ),
            ),
          )
        ],
      ),
    ],
  ),
  // Drawer widget creates a side navigation menu.
  drawer: Drawer(
    child: ListView(
      padding: EdgeInsets.zero,
      children: [
        const DrawerHeader(
          decoration: BoxDecoration(
            color: AppColors.eggPlant, // Header background color.
          ),
          child: Text(
            'Menu',
            style: TextStyle(
              color: AppColors.babyPowder, // Header text color.
              fontSize: 24,
            ),
          ),
        ),
        ListTile(
          leading: const Icon(Icons.home),
          title: const Text('Home'),
          onTap: () {
            Navigator.pop(context); // Close the drawer when Home is tapped.
          },
        ),
        ListTile(
          leading: const Icon(Icons.calendar_today),
          title: const Text('Calendar'),
          onTap: () {
            Navigator.pop(context); // Close the drawer when Calendar is tapped.
          },
        ),
        ListTile(
          leading: const Icon(Icons.settings),
          title: const Text('Settings'),
          onTap: () {
            Navigator.pop(context); // Close the drawer when Settings is tapped.
          },
        ),
      ],
    ),
  ),
  // Floating action button with an edit icon.
  floatingActionButton: FloatingActionButton(
    onPressed: () => _navigateToAddEventScreen(), // Define the onPressed action.
    child: Icon(
      Icons.edit,
      color: Colors.black,
    ),
    backgroundColor: AppColors.babyPowder, // Background color of the button.
  ),
  body: SingleChildScrollView(
    child: Column(
      children: [
        // Card widget for displaying content with a card-like design.
        Card(
          margin: const EdgeInsets.all(8.0),
          elevation: 5.0,
          shape: const RoundedRectangleBorder(
            borderRadius: BorderRadius.all(
              Radius.circular(10),
            ),
            side: BorderSide(
                color: AppColors.blackCoffee, width: 2.0), // Card border.
          ),
          child: TableCalendar(
            calendarFormat: _calendarFormat, // Define the calendar format.
  
            focusedDay: _focusedCalendarDate, // Set the focused calendar day.
            // today's date
            firstDay: _initialCalendarDate, // Set the earliest possible date.
            lastDay: _lastCalendarDate, // Set the latest allowed date.
              
            // ... (Additional calendar settings and styles)
              
            // Event loader function to load events for specific dates.
  
            // ... (Header style and other calendar styles)
              
            selectedDayPredicate: (currentSelectedDate) {
              // Predicate to determine the currently selected day.
              return (isSameDay(selectedCalendarDate!, currentSelectedDate));
            },
            onDaySelected: (selectedDay, focusedDay) {
              // Action when a day is selected.
              if (!isSameDay(selectedCalendarDate, selectedDay)) {
                setState(() {
                  selectedCalendarDate = selectedDay;
                  _focusedCalendarDate = focusedDay;
                });
              }
            },
          ),
        ),
        // Map over a list of events and display them as ListTiles.
  
        // ... (List item styling and onTap actions)
         ..._listOfDayEvents(selectedCalendarDate!).map(
              (myEvents) => GestureDetector(
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => DetailScreen(
                        eventTitle: myEvents.eventTitle,
                        eventDescp: myEvents.eventDescp,
                        eventDate: selectedCalendarDate!,
                      ),
                    ),
                  );
                },
                child: Padding(
                  padding: const EdgeInsets.symmetric(
                      horizontal: 12.0, vertical: 8.0),
                  child: ListTile(
                    // add rounded border to list item
                    shape: RoundedRectangleBorder(
                      side: BorderSide(color: AppColors.eggPlant, width: 2.0),
                      borderRadius: BorderRadius.circular(10.0),
                    ),
  
                    leading: Container(
                      width: 50,
                      height: 50,
                      decoration: BoxDecoration(
                        color: AppColors.eggPlant,
                        shape: BoxShape.circle,
                      ),
                      child: const Icon(
                        Icons.done,
                        color: AppColors.babyPowder,
                      ),
                    ),
                    // in trailing add forward icon
                    trailing: const Icon(
                      Icons.arrow_forward_ios,
                      color: AppColors.eggPlant,
                    ),
  
                    title: Padding(
                      padding: const EdgeInsets.only(bottom: 8.0),
                      child: Text(
                        '${myEvents.eventTitle}',
                        style: TextStyle(
                            fontSize: 20, fontWeight: FontWeight.bold),
                      ),
                    ),
                    subtitle: Text('${myEvents.eventDescp}'),
                  ),
                ),
              ),
            ),
      ],
    ),
  ),
);

Here we will try to explain the flow of code from top to bottom:

AppBar Configuration:

Drawer Configuration:

Floating Action Button Configuration:

Body Configuration:

List of Events:

The above code represents the structure and layout of a Flutter app screen, including an app bar, navigation drawer, calendar, and a list of events. Below we will write the function that will add on to the functionality of the app.




// This function is used to navigate to the 'SecondScreen' and handle the result.
void _navigateToAddEventScreen() async {
  // Use Navigator.push to navigate to 'SecondScreen'.
  final result = await Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondScreen()), // Create the route.
  );
  
  // Check if a result is returned and if it is of type 'MyEvents'.
  if (result != null && result is MyEvents) {
    // Retrieve the list of events for
    // the selected calendar date 
    // or initialize an empty list.
    final selectedDateEvents = mySelectedEvents[selectedCalendarDate] ?? [];
  
    // Update the list of events with 
    // the result from 'SecondScreen'.
    setState(() {
      // Add the new event to the list
      selectedDateEvents.add(result); 
        
      // Update the events for the selected date
      mySelectedEvents[selectedCalendarDate!] = selectedDateEvents; 
    });
  }
}

This code defines a function _navigateToAddEventScreen that handles navigation to the ‘SecondScreen’ and manages the result returned from that screen. It checks if a result is received and if it’s of type ‘MyEvents’. If so, it updates the list of events for the selected calendar date.




// This function displays an AlertDialog to add a new event.
_showAddEventDialog() async {
  // Show the dialog with title and content.
  await showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('New Event'), // Dialog title.
      content: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisSize: MainAxisSize.min,
        children: [
          // Create a text field for entering the event title.
          buildTextField(
            controller: titleController, 
            hint: 'Enter Title'
          ),
          const SizedBox(
            height: 20.0,
          ),
          // Create a text field for entering the event description.
          buildTextField(
            controller: descpController, 
            hint: 'Enter Description'
          ),
        ],
      ),
      actions: [
        // Cancel button to close the dialog.
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('Cancel'),
        ),
        // Add button to add the event.
        TextButton(
          onPressed: () {
            // Check if both title and description fields are empty.
            if (titleController.text.isEmpty && descpController.text.isEmpty) {
              // Show a snackbar with a message.
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('Please enter title & description'),
                  duration: Duration(seconds: 3),
                ),
              );
              return; // Exit the function.
            } else {
              setState(() {
                // Check if events for the selected date exist.
                if (mySelectedEvents[selectedCalendarDate] != null) {
                  // Add the new event to the existing events list.
                  mySelectedEvents[selectedCalendarDate]?.add(MyEvents(
                    eventTitle: titleController.text,
                    eventDescp: descpController.text,
                  ));
                } else {
                  // Create a new events list for the selected date.
                  mySelectedEvents[selectedCalendarDate!] = [
                    MyEvents(
                      eventTitle: titleController.text,
                      eventDescp: descpController.text,
                    ),
                  ];
                }
                print('Added event on date: $selectedCalendarDate');
              });
  
              titleController.clear(); // Clear the title field.
              descpController.clear(); // Clear the description field.
  
              Navigator.pop(context); // Close the dialog.
              return; // Exit the function.
            }
          },
          child: const Text('Add'), // Text for the Add button.
        ),
      ],
    ),
  );
}

This function creates an AlertDialog for adding a new event, including fields for entering the event title and description. It also handles validation and adds the event to the appropriate list when the “Add” button is pressed.




// This function creates and returns a TextField widget with specified properties.
Widget buildTextField({String? hint, required TextEditingController controller}) {
  // Create a TextField widget with the provided controller.
  return TextField(
    controller: controller,
    textCapitalization: TextCapitalization.words, // Capitalize each word in the input.
    decoration: InputDecoration(
      labelText: hint ?? '', // Set the label text to the provided hint, or empty if not provided.
      focusedBorder: OutlineInputBorder(
        // Define the border when the TextField is focused.
        borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5), // Border color and width.
        borderRadius: BorderRadius.circular(10.0), // Border radius.
      ),
      enabledBorder: OutlineInputBorder(
        // Define the border when the TextField is not focused.
        borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5), // Border color and width.
        borderRadius: BorderRadius.circular(10.0), // Border radius.
      ),
    ),
  );
}

This function creates a reusable TextField widget with specific properties. It can be used to build text input fields throughout the application. The function allows you to customize the hint text, controller, and text capitalization while providing consistent border styling for focused and non-focused states.

This concludes our apps home page. Now we will work on the screen from where we can add events.

create_event_view.dart

Import Statements:




import 'package:assignment_one/app/modules/home/views/home_view.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/create_event_controller.dart';

In this section, you import necessary packages and modules required for this Dart file. This includes Flutter’s material.dart for UI components and get.dart for state management. You also import home_view.dart and a custom controller (create_event_controller.dart) that appear to be used elsewhere in the code.

SecondScreen Class Definition:




class SecondScreen extends StatefulWidget {
  @override
  _SecondScreenState createState() => _SecondScreenState();
}

Here, you define a SecondScreen class that extends StatefulWidget. This class represents the screen where users can add events.

_SecondScreenState Class Definition:




class _SecondScreenState extends State<SecondScreen> {
  // Create TextEditingController instances for handling input fields.
  final TextEditingController titleController = TextEditingController();
  final TextEditingController descpController = TextEditingController();

This section defines the _SecondScreenState class, which extends State<_SecondScreenState>. In this class, you create two TextEditingController instances named titleController and descpController. These controllers are used to manage the text input fields for event title and description.

dispose() Method:




@override
void dispose() {
  // Dispose of the TextEditingController instances to free resources.
  titleController.dispose();
  descpController.dispose();
  super.dispose();
}

The dispose() method is an overridden method that gets called when the state object is no longer needed. In this case, it disposes of the text controllers to release resources and prevent memory leaks.

Widget build(BuildContext context) Method:




@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Add Event'), // Set the app bar title.
    ),

This is the core of your widget, the build method. It returns a Scaffold widget, which provides the basic structure for your screen. The AppBar widget is used to display a navigation bar with the title “Add Event.”

Body of the Screen:




body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'Add Event',
              style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
       ),

Inside the Scaffold, you have the screen’s body. This includes a Padding widget to provide space around the content. Within the padding, you have a Column widget, which arranges its children vertically.

Text Widget – “Add Event” Title:




Text(
    'Add Event',
     style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
),

Here, you display the text “Add Event” as a title for this screen. You set the text style with a larger font size and bold weight.

Text Input Fields:




// Create and display a text input field for entering the event title.
buildTextField(controller: titleController, hint: 'Enter Title'),
SizedBox(height: 20.0),
// Create and display a text input field for entering the event description.
buildTextField(controller: descpController, hint: 'Enter Description'),

These lines create and display two text input fields for entering the event title and description. The buildTextField function is called to create these input fields. The titleController and descpController are provided as controllers for managing the text input.

Add Event Button:




ElevatedButton(
              onPressed: () {
                // Check if either title or description is empty.
                if (titleController.text.isEmpty || descpController.text.isEmpty) {
                  // Show a snackbar with a message if fields are empty.
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text('Please enter title and description'),
                      duration: Duration(seconds: 3),
                    ),
                  );
                } else {
                  // Create an event object with the entered title and description.
                  final event = MyEvents(
                    eventTitle: titleController.text,
                    eventDescp: descpController.text,
                  );
  
                  // Pop the current screen and pass the event object as the result.
                  Navigator.pop(context, event);
                }
              },
              child: Text('Add Event'), // Text for the button.
            ),
          ],
        ),
      ),
   );
}

In this section, you create an “Add Event” button as an ElevatedButton. When pressed, it checks if either the title or description fields are empty. If they are empty, it shows a snackbar with an error message. Otherwise, it creates an event object with the entered title and description, and then it pops (closes) the current screen and passes the event object back to the previous screen as the result.

buildTextField() Function:




// Widget function to create and return a text input field.
Widget buildTextField(
    {required TextEditingController controller, String? hint}) {
  return TextField(
    controller: controller,
    decoration: InputDecoration(
      labelText: hint ?? '', // Set the label text to the provided hint or empty.
      border: OutlineInputBorder(), // Define input field border.
    ),
  );
}

This is a separate function, buildTextField(), used to create and return a text input field. It takes a TextEditingController and an optional hint as parameters and returns a TextField widget with the provided controller and decoration.

How we can add events on the from this screen which will get reflected on the home of the app.

Now, lets quickly code the deail screen, which will show the detail of the events which are tapped.

detail_screen_view.dart

Import Statements:




import 'package:intl/intl.dart';
import '../controllers/detail_screen_controller.dart';

Class Definition:




class DetailScreen extends StatelessWidget {
  final String eventTitle;
  final String eventDescp;
  final DateTime eventDate;
    
  DetailScreen({
    required this.eventTitle,
    required this.eventDescp,
    required this.eventDate,
  });

Build Method:




@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Event Details'),
    ),
    body: Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        // ... Widget tree for displaying event details
      ],
    ),
  );
}

Event Image:




Padding(
  padding: const EdgeInsets.only(top: 12, bottom: 5, right: 12, left: 12),
  child: Container(
    height: 200.0,
    width: double.infinity,
    decoration: const BoxDecoration(
      color: AppColors.eggPlant,
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(10),
        topRight: Radius.circular(10),
        bottomLeft: Radius.circular(10),
        bottomRight: Radius.circular(10),
      ),
      image: DecorationImage(
        image: AssetImage('assets/event.jpg'),
        fit: BoxFit.cover,
      ),
    ),
  ),
),

Event Details:




Container(
  decoration: BoxDecoration(
    color: const Color.fromARGB(255, 53, 53, 53),
    borderRadius: BorderRadius.circular(10),
  ),
  padding: EdgeInsets.all(20),
  margin: EdgeInsets.symmetric(horizontal: 12, vertical: 5),
  child: Column(
    children: [
      // ... Widget tree for displaying event details
    ],
  ),
),

Event Title:




RichText(
  text: TextSpan(
    text: 'Event: ',
    style: TextStyle(
      fontSize: 22,
      fontWeight: FontWeight.bold,
      color: AppColors.eggPlant,
    ),
    children: <TextSpan>[
      TextSpan(
        text: '$eventTitle',
        style: TextStyle(
          fontSize: 22,
          fontWeight: FontWeight.bold,
          color: Colors.white,
        ),
      ),
    ],
  ),
),

Location and Date:




Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Row(
      children: [
        Icon(
          Icons.location_on,
          color: AppColors.eggPlant,
        ),
        Text('  Johansi Berline'),
      ],
    ),
    Row(
      children: [
        Icon(
          Icons.access_time,
          color: AppColors.eggPlant,
        ),
        Text('  ${DateFormat('yyyy-MM-dd').format(eventDate)}'),
      ],
    ),
  ],
),

Event Description:




SizedBox(
  child: Row(
    children: [
      SizedBox(
        width: 320,
        child: Text(
          'Description: $eventDescp',
          style: TextStyle(fontSize: 18, color: Colors.grey),
        ),
      ),
    ],
  ),
),

Action Button:




Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    OutlinedButton(
      // Cancel button styling
      onPressed: () {},
      child: Text(
        'Cancel',
        style: TextStyle(color: Colors.grey, fontSize: 20),
      ),
    ),
    OutlinedButton(
      // Join button styling
      onPressed: () {},
      child: Text(
        'Join',
        style: TextStyle(color: AppColors.eggPlant, fontSize: 20),
      ),
    ),
  ],
),

This section creates two action buttons: “Cancel” and “Join.”

This marks the completeness of the event detail screen. So this concluded the flutter event table calander.

Output:


Article Tags :