6 | #endif
7 |
8 | #include "flutter/generated_plugin_registrant.h"
9 |
10 | struct _MyApplication {
11 | GtkApplication parent_instance;
12 | char** dart_entrypoint_arguments;
13 | };
14 |
15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
16 |
17 | // Implements GApplication::activate.
18 | static void my_application_activate(GApplication* application) {
19 | MyApplication* self = MY_APPLICATION(application);
20 | GtkWindow* window =
21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
22 |
23 | // Use a header bar when running in GNOME as this is the common style used
24 | // by applications and is the setup most users will be using (e.g. Ubuntu
25 | // desktop).
26 | // If running on X and not using GNOME then just use a traditional title bar
27 | // in case the window manager does more exotic layout, e.g. tiling.
28 | // If running on Wayland assume the header bar will work (may need changing
29 | // if future cases occur).
30 | gboolean use_header_bar = TRUE;
31 | #ifdef GDK_WINDOWING_X11
32 | GdkScreen* screen = gtk_window_get_screen(window);
33 | if (GDK_IS_X11_SCREEN(screen)) {
34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
36 | use_header_bar = FALSE;
37 | }
38 | }
39 | #endif
40 | if (use_header_bar) {
41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
42 | gtk_widget_show(GTK_WIDGET(header_bar));
43 | gtk_header_bar_set_title(header_bar, "noti");
44 | gtk_header_bar_set_show_close_button(header_bar, TRUE);
45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
46 | } else {
47 | gtk_window_set_title(window, "noti");
48 | }
49 |
50 | gtk_window_set_default_size(window, 1280, 720);
51 | gtk_widget_show(GTK_WIDGET(window));
52 |
53 | g_autoptr(FlDartProject) project = fl_dart_project_new();
54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
55 |
56 | FlView* view = fl_view_new(project);
57 | gtk_widget_show(GTK_WIDGET(view));
58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
59 |
60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view));
61 |
62 | gtk_widget_grab_focus(GTK_WIDGET(view));
63 | }
64 |
65 | // Implements GApplication::local_command_line.
66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
67 | MyApplication* self = MY_APPLICATION(application);
68 | // Strip out the first argument as it is the binary name.
69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
70 |
71 | g_autoptr(GError) error = nullptr;
72 | if (!g_application_register(application, nullptr, &error)) {
73 | g_warning("Failed to register: %s", error->message);
74 | *exit_status = 1;
75 | return TRUE;
76 | }
77 |
78 | g_application_activate(application);
79 | *exit_status = 0;
80 |
81 | return TRUE;
82 | }
83 |
84 | // Implements GApplication::startup.
85 | static void my_application_startup(GApplication* application) {
86 | //MyApplication* self = MY_APPLICATION(object);
87 |
88 | // Perform any actions required at application startup.
89 |
90 | G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
91 | }
92 |
93 | // Implements GApplication::shutdown.
94 | static void my_application_shutdown(GApplication* application) {
95 | //MyApplication* self = MY_APPLICATION(object);
96 |
97 | // Perform any actions required at application shutdown.
98 |
99 | G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
100 | }
101 |
102 | // Implements GObject::dispose.
103 | static void my_application_dispose(GObject* object) {
104 | MyApplication* self = MY_APPLICATION(object);
105 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
106 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
107 | }
108 |
109 | static void my_application_class_init(MyApplicationClass* klass) {
110 | G_APPLICATION_CLASS(klass)->activate = my_application_activate;
111 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
112 | G_APPLICATION_CLASS(klass)->startup = my_application_startup;
113 | G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
114 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
115 | }
116 |
117 | static void my_application_init(MyApplication* self) {}
118 |
119 | MyApplication* my_application_new() {
120 | return MY_APPLICATION(g_object_new(my_application_get_type(),
121 | "application-id", APPLICATION_ID,
122 | "flags", G_APPLICATION_NON_UNIQUE,
123 | nullptr));
124 | }
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FireNoti
2 |
3 | FireNoti is a Flutter-based mobile application that leverages Firebase Cloud Messaging (FCM) for sending and managing push notifications. This project aims to provide a seamless notification experience by integrating Firebase FCM, enabling real-time alerts and updates for users.
4 |
5 | ## Features
6 |
7 | - **Firebase FCM Integration**: Uses Firebase Cloud Messaging for sending push notifications.
8 | - **Real-Time Notifications**: Receive instant alerts and updates directly on your mobile device.
9 | - **Customizable Alerts**: Allows easy customization of notification content and appearance.
10 | - **Efficient Message Delivery**: Ensures reliable and timely notification delivery.
11 | - **User Engagement**: Boosts user engagement with interactive notifications.
12 |
13 | # Fire-Noti Project Contribution Acknowledgement
14 |
15 |
16 |
17 |
18 |
19 | We would like to acknowledge the valuable contributions made by **Imran** to the Fire-Noti project.
20 |
21 | ## Updates by Imran
22 |
23 | - **iOS Project Configuration and Assets**
24 | - Added CocoaPods support by creating a `Podfile` and including necessary configurations in `Debug.xcconfig` and `Release.xcconfig`.
25 | - Updated `Runner.xcodeproj` to include Pods frameworks and configurations.
26 | - Added new app icon images to `Assets.xcassets` and updated `Contents.json` for proper asset management.
27 | - Modified `Info.plist` to include user tracking usage description and background modes for notifications.
28 | - Refactored `NotificationCalendar` class to allow mutable properties: year, month, day, hour, minute, second, and millisecond.
29 |
30 | Thank you, Imran, for your dedication and excellent work on improving the Fire-Noti project!
31 |
32 |
33 | ## Getting Started
34 |
35 | ### Prerequisites
36 |
37 | Before you begin, ensure you have met the following requirements:
38 | - [firebase_core](https://pub.dev/packages/firebase_core): For integrating Firebase with the Flutter app.
39 | - [firebase_messaging](https://pub.dev/packages/firebase_messaging): For Firebase Cloud Messaging (FCM) support.
40 | - [awesome_notifications](https://pub.dev/packages/awesome_notifications): For displaying customizable local and push notifications.
41 |
42 | Add these dependencies to your `pubspec.yaml` file:
43 |
44 | ```yaml
45 | dependencies:
46 | flutter:
47 | sdk: flutter
48 | firebase_core: ^3.6.0
49 | firebase_messaging: ^15.1.3
50 | awesome_notifications: ^0.10.0
51 | ```
52 | ### Installation
53 |
54 | 1. **Clone the repository:**
55 | ```bash
56 | git clone https://github.com/Puneetsharma5525/Fire-Noti.git
57 | cd FireNoti
58 | ### Android Output
59 |
60 | This is the main screen of the Flutter application, showcasing the primary user interface.
61 |
62 |
63 | Example of a simple notification that alerts the user to a new message or event.
64 |
65 |
66 | Demonstration of a notification with sound, enhancing user engagement through audio cues.
67 |
68 |
69 | An example of a big image notification.
70 |
71 |
72 | Illustration of a notification featuring action buttons, allowing users to respond directly from the notification.
73 |
74 |
75 | This shows a reply notification, enabling users to quickly respond to messages without opening the app.
76 |
77 |
78 | Example of a chat message notification, indicating new messages from contacts.
79 |
80 |
81 | Music notification showing playback controls and information about the currently playing track.
82 |
83 |
84 | A big text notification that provides detailed information and context to the user.
85 |
86 | ### Simulator Output
87 |
88 | ### 1. Home Screen with App Icon and Badge
89 |
90 | The main home screen of the app displays the **FireNoti** app icon, app name, and notification badge on iOS.
91 |
92 | ---
93 |
94 | ### 2. Notification with Image
95 |
96 | This shows a Flutter alert notification featuring an image alongside the notification message on iOS.
97 |
98 | ---
99 |
100 | ### 3. Notification with Logo and Image
101 |
102 | Here, the notification displays both a logo and a large image, providing a rich notification experience on iOS.
103 |
104 | ---
105 |
106 | ### 4. Home Screen with Various Notification Types
107 |
108 | The app's home screen demonstrates different notification types available on iOS, including:
109 | - Simple Notification
110 | - Sound Notification
111 | - Big Image Notification
112 | - Button Notification
113 | - Reply Message Notification
114 | - Scheduled Notification
115 | - Music Notification
116 | - Big Text Notification
117 |
118 |
--------------------------------------------------------------------------------
/linux/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Project-level configuration.
2 | cmake_minimum_required(VERSION 3.10)
3 | project(runner LANGUAGES CXX)
4 |
5 | # The name of the executable created for the application. Change this to change
6 | # the on-disk name of your application.
7 | set(BINARY_NAME "noti")
8 | # The unique GTK application identifier for this application. See:
9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID
10 | set(APPLICATION_ID "com.fire.noti")
11 |
12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
13 | # versions of CMake.
14 | cmake_policy(SET CMP0063 NEW)
15 |
16 | # Load bundled libraries from the lib/ directory relative to the binary.
17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
18 |
19 | # Root filesystem for cross-building.
20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT)
21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
27 | endif()
28 |
29 | # Define build configuration options.
30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
31 | set(CMAKE_BUILD_TYPE "Debug" CACHE
32 | STRING "Flutter build mode" FORCE)
33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
34 | "Debug" "Profile" "Release")
35 | endif()
36 |
37 | # Compilation settings that should be applied to most targets.
38 | #
39 | # Be cautious about adding new options here, as plugins use this function by
40 | # default. In most cases, you should add new options to specific targets instead
41 | # of modifying this function.
42 | function(APPLY_STANDARD_SETTINGS TARGET)
43 | target_compile_features(${TARGET} PUBLIC cxx_std_14)
44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror)
45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
47 | endfunction()
48 |
49 | # Flutter library and tool build rules.
50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
51 | add_subdirectory(${FLUTTER_MANAGED_DIR})
52 |
53 | # System-level dependencies.
54 | find_package(PkgConfig REQUIRED)
55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
56 |
57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
58 |
59 | # Define the application target. To change its name, change BINARY_NAME above,
60 | # not the value here, or `flutter run` will no longer work.
61 | #
62 | # Any new source files that you add to the application should be added here.
63 | add_executable(${BINARY_NAME}
64 | "main.cc"
65 | "my_application.cc"
66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
67 | )
68 |
69 | # Apply the standard set of build settings. This can be removed for applications
70 | # that need different build settings.
71 | apply_standard_settings(${BINARY_NAME})
72 |
73 | # Add dependency libraries. Add any application-specific dependencies here.
74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter)
75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
76 |
77 | # Run the Flutter tool portions of the build. This must not be removed.
78 | add_dependencies(${BINARY_NAME} flutter_assemble)
79 |
80 | # Only the install-generated bundle's copy of the executable will launch
81 | # correctly, since the resources must in the right relative locations. To avoid
82 | # people trying to run the unbundled copy, put it in a subdirectory instead of
83 | # the default top-level location.
84 | set_target_properties(${BINARY_NAME}
85 | PROPERTIES
86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
87 | )
88 |
89 |
90 | # Generated plugin build rules, which manage building the plugins and adding
91 | # them to the application.
92 | include(flutter/generated_plugins.cmake)
93 |
94 |
95 | # === Installation ===
96 | # By default, "installing" just makes a relocatable bundle in the build
97 | # directory.
98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
101 | endif()
102 |
103 | # Start with a clean build bundle directory every time.
104 | install(CODE "
105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
106 | " COMPONENT Runtime)
107 |
108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
110 |
111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
112 | COMPONENT Runtime)
113 |
114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
115 | COMPONENT Runtime)
116 |
117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
118 | COMPONENT Runtime)
119 |
120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
121 | install(FILES "${bundled_library}"
122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
123 | COMPONENT Runtime)
124 | endforeach(bundled_library)
125 |
126 | # Copy the native assets provided by the build.dart from all packages.
127 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
128 | install(DIRECTORY "${NATIVE_ASSETS_DIR}"
129 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
130 | COMPONENT Runtime)
131 |
132 | # Fully re-copy the assets directory on each build to avoid having stale files
133 | # from a previous install.
134 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
135 | install(CODE "
136 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
137 | " COMPONENT Runtime)
138 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
139 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
140 |
141 | # Install the AOT library on non-Debug builds only.
142 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
143 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
144 | COMPONENT Runtime)
145 | endif()
146 |
--------------------------------------------------------------------------------
/windows/runner/win32_window.cpp:
--------------------------------------------------------------------------------
1 | #include "win32_window.h"
2 |
3 | #include
4 | #include
5 |
6 | #include "resource.h"
7 |
8 | namespace {
9 |
10 | /// Window attribute that enables dark mode window decorations.
11 | ///
12 | /// Redefined in case the developer's machine has a Windows SDK older than
13 | /// version 10.0.22000.0.
14 | /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
15 | #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
16 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
17 | #endif
18 |
19 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
20 |
21 | /// Registry key for app theme preference.
22 | ///
23 | /// A value of 0 indicates apps should use dark mode. A non-zero or missing
24 | /// value indicates apps should use light mode.
25 | constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
26 | L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
27 | constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
28 |
29 | // The number of Win32Window objects that currently exist.
30 | static int g_active_window_count = 0;
31 |
32 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
33 |
34 | // Scale helper to convert logical scaler values to physical using passed in
35 | // scale factor
36 | int Scale(int source, double scale_factor) {
37 | return static_cast(source * scale_factor);
38 | }
39 |
40 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
41 | // This API is only needed for PerMonitor V1 awareness mode.
42 | void EnableFullDpiSupportIfAvailable(HWND hwnd) {
43 | HMODULE user32_module = LoadLibraryA("User32.dll");
44 | if (!user32_module) {
45 | return;
46 | }
47 | auto enable_non_client_dpi_scaling =
48 | reinterpret_cast(
49 | GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
50 | if (enable_non_client_dpi_scaling != nullptr) {
51 | enable_non_client_dpi_scaling(hwnd);
52 | }
53 | FreeLibrary(user32_module);
54 | }
55 |
56 | } // namespace
57 |
58 | // Manages the Win32Window's window class registration.
59 | class WindowClassRegistrar {
60 | public:
61 | ~WindowClassRegistrar() = default;
62 |
63 | // Returns the singleton registrar instance.
64 | static WindowClassRegistrar* GetInstance() {
65 | if (!instance_) {
66 | instance_ = new WindowClassRegistrar();
67 | }
68 | return instance_;
69 | }
70 |
71 | // Returns the name of the window class, registering the class if it hasn't
72 | // previously been registered.
73 | const wchar_t* GetWindowClass();
74 |
75 | // Unregisters the window class. Should only be called if there are no
76 | // instances of the window.
77 | void UnregisterWindowClass();
78 |
79 | private:
80 | WindowClassRegistrar() = default;
81 |
82 | static WindowClassRegistrar* instance_;
83 |
84 | bool class_registered_ = false;
85 | };
86 |
87 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
88 |
89 | const wchar_t* WindowClassRegistrar::GetWindowClass() {
90 | if (!class_registered_) {
91 | WNDCLASS window_class{};
92 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
93 | window_class.lpszClassName = kWindowClassName;
94 | window_class.style = CS_HREDRAW | CS_VREDRAW;
95 | window_class.cbClsExtra = 0;
96 | window_class.cbWndExtra = 0;
97 | window_class.hInstance = GetModuleHandle(nullptr);
98 | window_class.hIcon =
99 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
100 | window_class.hbrBackground = 0;
101 | window_class.lpszMenuName = nullptr;
102 | window_class.lpfnWndProc = Win32Window::WndProc;
103 | RegisterClass(&window_class);
104 | class_registered_ = true;
105 | }
106 | return kWindowClassName;
107 | }
108 |
109 | void WindowClassRegistrar::UnregisterWindowClass() {
110 | UnregisterClass(kWindowClassName, nullptr);
111 | class_registered_ = false;
112 | }
113 |
114 | Win32Window::Win32Window() {
115 | ++g_active_window_count;
116 | }
117 |
118 | Win32Window::~Win32Window() {
119 | --g_active_window_count;
120 | Destroy();
121 | }
122 |
123 | bool Win32Window::Create(const std::wstring& title,
124 | const Point& origin,
125 | const Size& size) {
126 | Destroy();
127 |
128 | const wchar_t* window_class =
129 | WindowClassRegistrar::GetInstance()->GetWindowClass();
130 |
131 | const POINT target_point = {static_cast(origin.x),
132 | static_cast(origin.y)};
133 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
134 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
135 | double scale_factor = dpi / 96.0;
136 |
137 | HWND window = CreateWindow(
138 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
139 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
140 | Scale(size.width, scale_factor), Scale(size.height, scale_factor),
141 | nullptr, nullptr, GetModuleHandle(nullptr), this);
142 |
143 | if (!window) {
144 | return false;
145 | }
146 |
147 | UpdateTheme(window);
148 |
149 | return OnCreate();
150 | }
151 |
152 | bool Win32Window::Show() {
153 | return ShowWindow(window_handle_, SW_SHOWNORMAL);
154 | }
155 |
156 | // static
157 | LRESULT CALLBACK Win32Window::WndProc(HWND const window,
158 | UINT const message,
159 | WPARAM const wparam,
160 | LPARAM const lparam) noexcept {
161 | if (message == WM_NCCREATE) {
162 | auto window_struct = reinterpret_cast(lparam);
163 | SetWindowLongPtr(window, GWLP_USERDATA,
164 | reinterpret_cast(window_struct->lpCreateParams));
165 |
166 | auto that = static_cast(window_struct->lpCreateParams);
167 | EnableFullDpiSupportIfAvailable(window);
168 | that->window_handle_ = window;
169 | } else if (Win32Window* that = GetThisFromHandle(window)) {
170 | return that->MessageHandler(window, message, wparam, lparam);
171 | }
172 |
173 | return DefWindowProc(window, message, wparam, lparam);
174 | }
175 |
176 | LRESULT
177 | Win32Window::MessageHandler(HWND hwnd,
178 | UINT const message,
179 | WPARAM const wparam,
180 | LPARAM const lparam) noexcept {
181 | switch (message) {
182 | case WM_DESTROY:
183 | window_handle_ = nullptr;
184 | Destroy();
185 | if (quit_on_close_) {
186 | PostQuitMessage(0);
187 | }
188 | return 0;
189 |
190 | case WM_DPICHANGED: {
191 | auto newRectSize = reinterpret_cast(lparam);
192 | LONG newWidth = newRectSize->right - newRectSize->left;
193 | LONG newHeight = newRectSize->bottom - newRectSize->top;
194 |
195 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
196 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
197 |
198 | return 0;
199 | }
200 | case WM_SIZE: {
201 | RECT rect = GetClientArea();
202 | if (child_content_ != nullptr) {
203 | // Size and position the child window.
204 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
205 | rect.bottom - rect.top, TRUE);
206 | }
207 | return 0;
208 | }
209 |
210 | case WM_ACTIVATE:
211 | if (child_content_ != nullptr) {
212 | SetFocus(child_content_);
213 | }
214 | return 0;
215 |
216 | case WM_DWMCOLORIZATIONCOLORCHANGED:
217 | UpdateTheme(hwnd);
218 | return 0;
219 | }
220 |
221 | return DefWindowProc(window_handle_, message, wparam, lparam);
222 | }
223 |
224 | void Win32Window::Destroy() {
225 | OnDestroy();
226 |
227 | if (window_handle_) {
228 | DestroyWindow(window_handle_);
229 | window_handle_ = nullptr;
230 | }
231 | if (g_active_window_count == 0) {
232 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
233 | }
234 | }
235 |
236 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
237 | return reinterpret_cast(
238 | GetWindowLongPtr(window, GWLP_USERDATA));
239 | }
240 |
241 | void Win32Window::SetChildContent(HWND content) {
242 | child_content_ = content;
243 | SetParent(content, window_handle_);
244 | RECT frame = GetClientArea();
245 |
246 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
247 | frame.bottom - frame.top, true);
248 |
249 | SetFocus(child_content_);
250 | }
251 |
252 | RECT Win32Window::GetClientArea() {
253 | RECT frame;
254 | GetClientRect(window_handle_, &frame);
255 | return frame;
256 | }
257 |
258 | HWND Win32Window::GetHandle() {
259 | return window_handle_;
260 | }
261 |
262 | void Win32Window::SetQuitOnClose(bool quit_on_close) {
263 | quit_on_close_ = quit_on_close;
264 | }
265 |
266 | bool Win32Window::OnCreate() {
267 | // No-op; provided for subclasses.
268 | return true;
269 | }
270 |
271 | void Win32Window::OnDestroy() {
272 | // No-op; provided for subclasses.
273 | }
274 |
275 | void Win32Window::UpdateTheme(HWND const window) {
276 | DWORD light_mode;
277 | DWORD light_mode_size = sizeof(light_mode);
278 | LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
279 | kGetPreferredBrightnessRegValue,
280 | RRF_RT_REG_DWORD, nullptr, &light_mode,
281 | &light_mode_size);
282 |
283 | if (result == ERROR_SUCCESS) {
284 | BOOL enable_dark_mode = light_mode == 0;
285 | DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
286 | &enable_dark_mode, sizeof(enable_dark_mode));
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | _flutterfire_internals:
5 | dependency: transitive
6 | description:
7 | name: _flutterfire_internals
8 | sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "1.3.44"
12 | async:
13 | dependency: transitive
14 | description:
15 | name: async
16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.11.0"
20 | awesome_notifications:
21 | dependency: "direct main"
22 | description:
23 | name: awesome_notifications
24 | sha256: d051ffb694a53da216ff13d02c8ec645d75320048262f7e6b3c1d95a4f54c902
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "0.10.0"
28 | boolean_selector:
29 | dependency: transitive
30 | description:
31 | name: boolean_selector
32 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "2.1.1"
36 | characters:
37 | dependency: transitive
38 | description:
39 | name: characters
40 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.3.0"
44 | clock:
45 | dependency: transitive
46 | description:
47 | name: clock
48 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.1.1"
52 | collection:
53 | dependency: transitive
54 | description:
55 | name: collection
56 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.18.0"
60 | fake_async:
61 | dependency: transitive
62 | description:
63 | name: fake_async
64 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
65 | url: "https://pub.dev"
66 | source: hosted
67 | version: "1.3.1"
68 | firebase_core:
69 | dependency: "direct main"
70 | description:
71 | name: firebase_core
72 | sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96"
73 | url: "https://pub.dev"
74 | source: hosted
75 | version: "3.6.0"
76 | firebase_core_platform_interface:
77 | dependency: transitive
78 | description:
79 | name: firebase_core_platform_interface
80 | sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810
81 | url: "https://pub.dev"
82 | source: hosted
83 | version: "5.3.0"
84 | firebase_core_web:
85 | dependency: transitive
86 | description:
87 | name: firebase_core_web
88 | sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5
89 | url: "https://pub.dev"
90 | source: hosted
91 | version: "2.18.1"
92 | firebase_messaging:
93 | dependency: "direct main"
94 | description:
95 | name: firebase_messaging
96 | sha256: eb6e28a3a35deda61fe8634967c84215efc19133ba58d8e0fc6c9a2af2cba05e
97 | url: "https://pub.dev"
98 | source: hosted
99 | version: "15.1.3"
100 | firebase_messaging_platform_interface:
101 | dependency: transitive
102 | description:
103 | name: firebase_messaging_platform_interface
104 | sha256: b316c4ee10d93d32c033644207afc282d9b2b4372f3cf9c6022f3558b3873d2d
105 | url: "https://pub.dev"
106 | source: hosted
107 | version: "4.5.46"
108 | firebase_messaging_web:
109 | dependency: transitive
110 | description:
111 | name: firebase_messaging_web
112 | sha256: d7f0147a1a9fe4313168e20154a01fd5cf332898de1527d3930ff77b8c7f5387
113 | url: "https://pub.dev"
114 | source: hosted
115 | version: "3.9.2"
116 | flutter:
117 | dependency: "direct main"
118 | description: flutter
119 | source: sdk
120 | version: "0.0.0"
121 | flutter_lints:
122 | dependency: "direct dev"
123 | description:
124 | name: flutter_lints
125 | sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
126 | url: "https://pub.dev"
127 | source: hosted
128 | version: "4.0.0"
129 | flutter_test:
130 | dependency: "direct dev"
131 | description: flutter
132 | source: sdk
133 | version: "0.0.0"
134 | flutter_web_plugins:
135 | dependency: transitive
136 | description: flutter
137 | source: sdk
138 | version: "0.0.0"
139 | intl:
140 | dependency: transitive
141 | description:
142 | name: intl
143 | sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "0.19.0"
147 | leak_tracker:
148 | dependency: transitive
149 | description:
150 | name: leak_tracker
151 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "10.0.5"
155 | leak_tracker_flutter_testing:
156 | dependency: transitive
157 | description:
158 | name: leak_tracker_flutter_testing
159 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "3.0.5"
163 | leak_tracker_testing:
164 | dependency: transitive
165 | description:
166 | name: leak_tracker_testing
167 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "3.0.1"
171 | lints:
172 | dependency: transitive
173 | description:
174 | name: lints
175 | sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "4.0.0"
179 | matcher:
180 | dependency: transitive
181 | description:
182 | name: matcher
183 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "0.12.16+1"
187 | material_color_utilities:
188 | dependency: transitive
189 | description:
190 | name: material_color_utilities
191 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "0.11.1"
195 | meta:
196 | dependency: transitive
197 | description:
198 | name: meta
199 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
200 | url: "https://pub.dev"
201 | source: hosted
202 | version: "1.15.0"
203 | path:
204 | dependency: transitive
205 | description:
206 | name: path
207 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
208 | url: "https://pub.dev"
209 | source: hosted
210 | version: "1.9.0"
211 | plugin_platform_interface:
212 | dependency: transitive
213 | description:
214 | name: plugin_platform_interface
215 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
216 | url: "https://pub.dev"
217 | source: hosted
218 | version: "2.1.8"
219 | sky_engine:
220 | dependency: transitive
221 | description: flutter
222 | source: sdk
223 | version: "0.0.99"
224 | source_span:
225 | dependency: transitive
226 | description:
227 | name: source_span
228 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
229 | url: "https://pub.dev"
230 | source: hosted
231 | version: "1.10.0"
232 | stack_trace:
233 | dependency: transitive
234 | description:
235 | name: stack_trace
236 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
237 | url: "https://pub.dev"
238 | source: hosted
239 | version: "1.11.1"
240 | stream_channel:
241 | dependency: transitive
242 | description:
243 | name: stream_channel
244 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
245 | url: "https://pub.dev"
246 | source: hosted
247 | version: "2.1.2"
248 | string_scanner:
249 | dependency: transitive
250 | description:
251 | name: string_scanner
252 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
253 | url: "https://pub.dev"
254 | source: hosted
255 | version: "1.2.0"
256 | term_glyph:
257 | dependency: transitive
258 | description:
259 | name: term_glyph
260 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
261 | url: "https://pub.dev"
262 | source: hosted
263 | version: "1.2.1"
264 | test_api:
265 | dependency: transitive
266 | description:
267 | name: test_api
268 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
269 | url: "https://pub.dev"
270 | source: hosted
271 | version: "0.7.2"
272 | vector_math:
273 | dependency: transitive
274 | description:
275 | name: vector_math
276 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
277 | url: "https://pub.dev"
278 | source: hosted
279 | version: "2.1.4"
280 | vm_service:
281 | dependency: transitive
282 | description:
283 | name: vm_service
284 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
285 | url: "https://pub.dev"
286 | source: hosted
287 | version: "14.2.5"
288 | web:
289 | dependency: transitive
290 | description:
291 | name: web
292 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
293 | url: "https://pub.dev"
294 | source: hosted
295 | version: "1.1.0"
296 | sdks:
297 | dart: ">=3.5.3 <4.0.0"
298 | flutter: ">=3.22.0"
299 |
--------------------------------------------------------------------------------
/lib/notification/awesome_notification/awesome_notification_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:awesome_notifications/awesome_notifications.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:noti/presentation/main_screen.dart';
5 | import '../../../main.dart';
6 | import '../../core/router/app_router.dart';
7 | import '../model/button_model.dart';
8 |
9 | // Private constants and fields
10 | const String _buttonKey1 = 'newPage';
11 | const String _buttonKey2 = 'reply';
12 | const String _buttonKey3 = 'toast';
13 |
14 | /// Handles actions when a notification button is pressed.
15 | @pragma('vm:entry-point')
16 | Future _onActionReceivedMethod(ReceivedAction event) async {
17 | BuildContext? context = MyApp.navigatorObserver.navigator?.context;
18 | if (event.buttonKeyPressed == _buttonKey1) {
19 | _navigateToNewRequestScreen();
20 | } else if (event.buttonKeyPressed == _buttonKey2 && context != null) {
21 | showMessage(
22 | msg: '''Your Reply is :- ${event.buttonKeyInput} ''', context: context);
23 | } else if (context != null && event.buttonKeyPressed == _buttonKey3) {
24 | showMessage(
25 | msg:
26 | '''Why do programmers prefer dark mode?\nBecause light attracts bugs! 🐛💻 ''',
27 | context: context);
28 | }
29 | }
30 |
31 | /// Service for handling notifications using Awesome Notifications package.
32 | class AwesomeNotificationService {
33 | final List? buttonList;
34 | AwesomeNotificationService({this.buttonList});
35 |
36 | /// Creates a notification with optional action buttons and a big picture.
37 | ///
38 | /// [id] - The ID for the notification (default is an empty string).
39 | /// [uid] - The unique ID of the notification.
40 | /// [title] - The title of the notification.
41 | /// [body] - The body content of the notification.
42 | /// [bigPicture] - The URL or asset path of the big picture (optional).
43 | /// [timeoutAfter] - Duration after which the notification should be dismissed (optional).
44 | /// [payload] - Custom payload data (optional).
45 | /// [buttonList] - List of action buttons for the notification (optional).
46 | /// [wakeUpScreen] - If true, wakes up the screen when the notification is received.
47 | /// [notificationLayout] - The layout of the notification.
48 | /// [channelKey] - The channel key for categorizing the notification.
49 | Future createNotification({
50 | String? id,
51 | int? uid,
52 | String? title,
53 | bool locked = false,
54 | String? body,
55 | String? bigPicture,
56 | String? customSound,
57 | String? largeIcon,
58 | bool repeats = false,
59 | DateTime? scheduledTime,
60 | bool preciseAlarm = false,
61 | Duration? timeoutAfter,
62 | Map? payload,
63 | bool wakeUpScreen = false,
64 | bool allowWhileIdle = false,
65 | NotificationLayout notificationLayout = NotificationLayout.Default,
66 | required String channelKey,
67 | }) async {
68 | NotificationSchedule? schedule;
69 | if (scheduledTime != null) {
70 | String localTimeZone =
71 | await AwesomeNotifications().getLocalTimeZoneIdentifier();
72 |
73 | // Create the notification calendar instance
74 | schedule = NotificationCalendar(
75 | timeZone: localTimeZone,
76 | year: scheduledTime.year,
77 | month: scheduledTime.month,
78 | day: scheduledTime.day,
79 | hour: scheduledTime.hour,
80 | minute: scheduledTime.minute,
81 | second: scheduledTime.second,
82 | millisecond: scheduledTime.millisecond,
83 | allowWhileIdle: allowWhileIdle,
84 | repeats: repeats,
85 | preciseAlarm: preciseAlarm,
86 | );
87 | }
88 | // Start listening for notification events
89 | _startListeningNotificationEvents();
90 |
91 | // Build action buttons from the buttonList
92 | List? actionButtons =
93 | _buildActionButtons(buttonList: buttonList);
94 |
95 | // Create the notification
96 | await AwesomeNotifications().createNotification(
97 | content: NotificationContent(
98 | id: uid ?? 0,
99 | channelKey: channelKey,
100 | title: title,
101 | body: body,
102 | wakeUpScreen: wakeUpScreen,
103 | bigPicture: bigPicture,
104 | largeIcon: largeIcon,
105 | payload: payload,
106 | locked: locked,
107 | timeoutAfter: timeoutAfter,
108 | customSound: customSound,
109 | notificationLayout: bigPicture != null
110 | ? NotificationLayout.BigPicture
111 | : notificationLayout,
112 | ),
113 | schedule: schedule,
114 | actionButtons: actionButtons,
115 | );
116 | }
117 |
118 | /// Creates a basic notification without action buttons.
119 | ///
120 | /// [uid] - The unique ID of the notification.
121 | /// [title] - The title of the notification.
122 | /// [body] - The body content of the notification.
123 | /// [channelKey] - The channel key for categorizing the notification.
124 | Future createNormalNotification({
125 | int? uid,
126 | String? title,
127 | String? body,
128 | required String channelKey,
129 | }) async {
130 | _startListeningNotificationEvents();
131 | await AwesomeNotifications().createNotification(
132 | content: NotificationContent(
133 | id: uid ?? 0,
134 | channelKey: channelKey,
135 | title: title,
136 | body: body,
137 | ),
138 | );
139 | }
140 |
141 | /// Schedules a notification to be triggered at a specified time.
142 | ///
143 | /// [uid] - The unique ID of the notification.
144 | /// [title] - The title of the notification.
145 | /// [body] - The body content of the notification.
146 | /// [channelKey] - The channel key for categorizing the notification.
147 | /// [scheduleTime] - The time when the notification should be shown.
148 | Future createScheduledNotification({
149 | int? uid,
150 | String? title,
151 | String? body,
152 | required String channelKey,
153 | required DateTime scheduleTime,
154 | }) async {
155 | _startListeningNotificationEvents();
156 | await AwesomeNotifications().createNotification(
157 | content: NotificationContent(
158 | id: uid ?? 0,
159 | channelKey: channelKey,
160 | title: title,
161 | body: body,
162 | ),
163 | schedule: NotificationCalendar.fromDate(date: scheduleTime),
164 | );
165 | }
166 |
167 | /// Updates the content of an existing notification.
168 | ///
169 | /// [notificationId] - The ID of the notification to be updated.
170 | /// [title] - The new title of the notification.
171 | /// [body] - The new body content of the notification.
172 | /// [bigPicture] - The new big picture URL or asset path (optional).
173 | /// [channelKey] - The channel key for categorizing the notification.
174 | Future updateNotification({
175 | required int notificationId,
176 | String? title,
177 | String? body,
178 | String? bigPicture,
179 | required String channelKey,
180 | }) async {
181 | await AwesomeNotifications().createNotification(
182 | content: NotificationContent(
183 | id: notificationId,
184 | channelKey: channelKey,
185 | title: title,
186 | body: body,
187 | bigPicture: bigPicture,
188 | notificationLayout: bigPicture != null
189 | ? NotificationLayout.BigPicture
190 | : NotificationLayout.Default,
191 | ),
192 | );
193 | }
194 |
195 | /// Cancels a notification by its ID.
196 | ///
197 | /// [notificationId] - The ID of the notification to cancel.
198 | Future cancelNotification(int notificationId) async {
199 | await AwesomeNotifications().cancel(notificationId);
200 | }
201 |
202 | /// Cancels all notifications.
203 | Future cancelAllNotifications() async {
204 | return await AwesomeNotifications().cancelAll();
205 | }
206 |
207 | /// Resets the global notification badge count.
208 | Future resetBadge() async {
209 | await AwesomeNotifications().resetGlobalBadge();
210 | }
211 |
212 | /// Checks if notifications are allowed and requests permission if not.
213 | Future checkNotificationPermission() async {
214 | bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
215 | if (!isAllowed) {
216 | await requestNotificationPermission();
217 | }
218 | }
219 |
220 | /// Requests notification permission from the user.
221 | Future requestNotificationPermission() async {
222 | await AwesomeNotifications().requestPermissionToSendNotifications();
223 | }
224 |
225 | /// Starts listening for notification action events.
226 | Future _startListeningNotificationEvents() {
227 | return AwesomeNotifications().setListeners(
228 | onActionReceivedMethod: _onActionReceivedMethod,
229 | );
230 | }
231 |
232 | /// Builds action buttons for notifications if required.
233 | ///
234 | /// [buttonModel] - A list of ButtonModel instances to create the action buttons.
235 | List? _buildActionButtons(
236 | {List? buttonList}) {
237 | if (buttonList?.isNotEmpty == true) {
238 | return buttonList?.map((element) {
239 | return NotificationActionButton(
240 | key: element.key,
241 | label: element.label,
242 | color: element.color,
243 | icon: element.icon,
244 | actionType: element.actionType,
245 | autoDismissible: element.autoDismissible,
246 | enabled: element.enabled,
247 | requireInputText: element.requireInputText,
248 | showInCompactView: element.showInCompactView,
249 | isDangerousOption: element.isDangerousOption,
250 | );
251 | }).toList();
252 | }
253 |
254 | return null;
255 | }
256 |
257 | NotificationActionButton createButton({
258 | required ButtonType buttonType,
259 | required String buttonKey,
260 | required String label,
261 | Color? color,
262 | String? icon,
263 | bool autoDismissible = true,
264 | bool enabled = true,
265 | }) {
266 | return NotificationActionButton(
267 | key: buttonKey,
268 | label: label,
269 | color: color,
270 | icon: icon,
271 | actionType: buttonType == ButtonType.custom
272 | ? ActionType.KeepOnTop
273 | : ActionType.Default,
274 | autoDismissible: autoDismissible,
275 | enabled: enabled,
276 | requireInputText: buttonType == ButtonType.message,
277 | showInCompactView: buttonType != ButtonType.custom,
278 | isDangerousOption: buttonType == ButtonType.custom,
279 | );
280 | }
281 | }
282 |
283 | void _navigateToNewRequestScreen() {
284 | BuildContext? context = MyApp.navigatorObserver.navigator?.context;
285 | if (context != null) {
286 | Navigator.pushNamed(context, AppRouter.newScreen, arguments: {
287 | 'first': 'Welcome! 🎉',
288 | 'second':
289 | 'We\'re so glad you\'re here! 🌟 Enjoy exploring and feel free to reach out if you need any assistance. 😊 Happy to have you with us!'
290 | });
291 | }
292 | }
293 |
--------------------------------------------------------------------------------