Skip to content

A modern, feature-rich calculator application built with Flutter. This app includes a clean UI, dark/light mode support, calculation history, and smooth animations.

Notifications You must be signed in to change notification settings

ElehGilbert/Nextgen_Calculator

Repository files navigation

NextGen Calculator

A modern, feature-rich calculator application built with Flutter. This app includes a clean UI, dark/light mode support, calculation history, and smooth animations.

Features

  • Basic Arithmetic Operations: Addition, subtraction, multiplication, and division
  • Advanced Functions: Percentage calculations, sign toggling
  • Dark/Light Mode: Toggle between themes
  • Calculation History: View and manage past calculations
  • Persistent Storage: History saved permanently on device
  • Custom App Icon: Branded launcher icon for professional appearance
  • Modern UI: Clean, minimalist design with smooth animations
  • Swipe to Delete: Remove individual history entries with a swipe gesture

Application ScreenShots

loading Screen Home Screen Light Mode Home Screen Dark mode Home Screen DarkMode History Screen DarkMode History Screen Delete history Clear History confirmation Popup

Architecture & State Management

State Management Pattern

The app uses the Provider pattern for state management, separating business logic from UI components.

Providers Used:

  1. ThemeProvider - Manages theme state (dark/light mode)
  2. HistoryManager - Manages calculation history state
  3. CalculatorLogic - Manages calculator computation state

Widgets & Components Documentation

1. Core App Structure

MaterialApp

  • Purpose: Root widget that provides Material Design styling and navigation
  • Usage in App: Configured with custom light and dark themes, sets up the home screen
  • Location: lib/main.dart
  • Key Properties:
    • theme: Light theme configuration
    • darkTheme: Dark theme configuration
    • themeMode: Current active theme mode from ThemeProvider

MultiProvider

  • Purpose: Allows multiple providers to be injected into the widget tree simultaneously
  • Usage in App: Provides both ThemeProvider and HistoryManager to all child widgets
  • Location: lib/main.dart
  • Why We Use It: Enables multiple state management objects to be accessible throughout the app

ChangeNotifierProvider

  • Purpose: Creates and provides a ChangeNotifier to descendant widgets
  • Usage in App:
    • Creates ThemeProvider instance for theme management
    • Creates HistoryManager instance for history management
  • Location: lib/main.dart

Consumer

  • Purpose: Rebuilds when the provider's data changes
  • Usage in App: Listens to ThemeProvider changes to rebuild the MaterialApp with updated theme
  • Location: lib/main.dart

2. Layout Widgets

Scaffold

  • Purpose: Provides basic Material Design layout structure
  • Usage in App:
    • Main container for the calculator screen (home.dart)
    • Main container for the history screen (history_screen.dart)
  • Key Properties Used:
    • backgroundColor: Sets the background color based on theme
    • body: Contains the main content
    • appBar: Top navigation bar (used in history screen)

SafeArea

  • Purpose: Ensures content is displayed within safe boundaries (avoiding notches, status bars)
  • Usage in App: Wraps the entire calculator interface to prevent overlap with system UI
  • Location: lib/home.dart

Column

  • Purpose: Arranges children vertically
  • Usage in App:
    • Main layout structure in calculator (top bar, display, button grid)
    • Display area (expression and result)
    • Button grid rows
    • Empty state in history screen
    • History card content
  • Key Properties Used:
    • mainAxisAlignment: Controls vertical alignment
    • crossAxisAlignment: Controls horizontal alignment
    • children: List of child widgets

Row

  • Purpose: Arranges children horizontally
  • Usage in App:
    • Top bar (dark mode toggle and history button)
    • Each row of calculator buttons
    • Content inside toggle/history buttons
  • Key Properties Used:
    • mainAxisAlignment: Controls horizontal spacing
    • children: List of child widgets

Expanded

  • Purpose: Expands a child to fill available space in a Row/Column
  • Usage in App:
    • Calculator display area (flex: 2)
    • Button grid area (flex: 3)
    • Individual calculator buttons to ensure equal sizing
  • Key Properties Used:
    • flex: Relative size compared to other Expanded widgets
    • child: Widget to expand

Container

  • Purpose: General-purpose box widget for styling and positioning
  • Usage in App:
    • Dark mode toggle button wrapper
    • History button wrapper
    • Display area wrapper
    • Button grid wrapper
    • Individual button containers
    • History card containers
    • Dismissible background
  • Key Properties Used:
    • padding: Inner spacing
    • margin: Outer spacing
    • decoration: Border, color, radius styling
    • alignment: Child alignment
    • width/height: Size constraints

Padding

  • Purpose: Adds padding around a widget
  • Usage in App: Top bar padding, button grid padding
  • Location: Throughout the app for spacing

SizedBox

  • Purpose: Fixed-size box, often used for spacing
  • Usage in App: Vertical spacing between display elements, horizontal spacing in buttons
  • Common Sizes: 8px, 16px for consistent spacing

3. Interactive Widgets

GestureDetector

  • Purpose: Detects gestures like taps, swipes, long presses
  • Usage in App:
    • Dark mode toggle button
    • History navigation button
  • Key Properties Used:
    • onTap: Callback when tapped
    • child: Widget to make interactive

Material & InkWell

  • Purpose:
    • Material: Provides Material Design surface
    • InkWell: Adds touch ripple effects
  • Usage in App: Calculator buttons for visual feedback on press
  • Key Properties Used:
    • Material: color, borderRadius
    • InkWell: onTap, borderRadius
  • Why We Use It: Creates professional Material Design ripple animations

IconButton

  • Purpose: Clickable icon with Material Design ripple
  • Usage in App: Back button in history screen AppBar
  • Key Properties Used:
    • icon: The icon to display
    • onPressed: Tap callback

TextButton

  • Purpose: Flat button with text
  • Usage in App:
    • "Clear All" button in history screen
    • Dialog action buttons (Cancel, Clear)
  • Key Properties Used:
    • onPressed: Tap callback
    • child: Button content (usually Text)

Dismissible

  • Purpose: Enables swipe-to-dismiss gesture
  • Usage in App: History entries can be swiped away to delete
  • Location: lib/history_screen.dart
  • Key Properties Used:
    • key: Unique identifier for each item
    • direction: Swipe direction (endToStart = right to left)
    • background: Widget shown when swiping
    • onDismissed: Callback when dismissed

4. Display Widgets

Text

  • Purpose: Displays text with styling
  • Usage in App:
    • Display value in calculator
    • Expression display
    • Button labels
    • History entries
    • Timestamps
    • Empty state messages
  • Key Properties Used:
    • style: TextStyle for font size, color, weight
    • maxLines: Maximum number of lines
    • overflow: How to handle overflow (ellipsis)

Icon

  • Purpose: Displays Material Design icons
  • Usage in App:
    • Dark/light mode icons
    • History icon
    • Back arrow
    • Delete icon in dismissible background
    • Empty state icon
  • Key Properties Used:
    • size: Icon size
    • color: Icon color
  • Icons Used:
    • Icons.dark_mode / Icons.light_mode
    • Icons.history
    • Icons.arrow_back
    • Icons.delete

AppBar

  • Purpose: Top navigation bar with title and actions
  • Usage in App: History screen navigation
  • Location: lib/history_screen.dart
  • Key Properties Used:
    • backgroundColor: Matches screen background
    • elevation: Shadow depth (0 for flat)
    • leading: Back button
    • title: "History" text
    • actions: "Clear All" button

5. Special-Purpose Widgets

ListenableBuilder

  • Purpose: Rebuilds when a Listenable (like ChangeNotifier) changes
  • Usage in App:
    • Rebuilds expression display when calculator state changes
    • Rebuilds main display value when calculator state changes
  • Location: lib/home.dart
  • Key Properties Used:
    • listenable: The ChangeNotifier to listen to (CalculatorLogic)
    • builder: Function that builds the widget

ListView.builder

  • Purpose: Efficiently builds scrollable lists
  • Usage in App: Displays calculation history with lazy loading
  • Location: lib/history_screen.dart
  • Key Properties Used:
    • padding: List padding
    • itemCount: Number of items
    • itemBuilder: Function to build each item

AlertDialog

  • Purpose: Shows a modal dialog
  • Usage in App: Confirmation dialog before clearing all history
  • Location: lib/history_screen.dart
  • Key Properties Used:
    • backgroundColor: Dialog background color
    • title: Dialog title
    • content: Dialog message
    • actions: Dialog buttons

Navigator

  • Purpose: Manages app navigation and routing
  • Usage in App:
    • Navigator.push: Navigate to history screen
    • Navigator.pop: Return to calculator screen, close dialogs
  • Locations: lib/home.dart, lib/history_screen.dart

Provider.of

  • Purpose: Accesses a provider from the widget tree
  • Usage in App:
    • Access ThemeProvider for theme state
    • Access HistoryManager for calculation history
  • Key Properties Used:
    • listen: Whether to rebuild when provider changes (true/false)

6. Styling Components

BoxDecoration

  • Purpose: Provides visual decoration for containers
  • Usage in App:
    • Button backgrounds with rounded corners
    • Card backgrounds for history entries
    • Dismissible background
  • Key Properties Used:
    • color: Background color
    • borderRadius: Rounded corners
    • border: Border styling (if needed)

BorderRadius

  • Purpose: Defines rounded corners
  • Usage in App: All buttons and cards use BorderRadius.circular(16) or BorderRadius.circular(20) for consistent rounded corners

TextStyle

  • Purpose: Defines text appearance
  • Usage in App: Font size, color, weight for all text elements
  • Key Properties Used:
    • fontSize: Text size (11-64px range)
    • color: Text color
    • fontWeight: Font weight (w300-w600)

Custom Classes & Models

CalculatorLogic (extends ChangeNotifier)

  • Purpose: Handles all calculator computation and state
  • File: lib/calculator_logic.dart
  • Key Methods:
    • onNumberPressed(): Handle digit input
    • onOperationPressed(): Handle operation (+, -, ×, ÷)
    • onEqualsPressed(): Perform calculation
    • onClearPressed(): Reset calculator
    • onBackspacePressed(): Delete last digit
    • onPercentPressed(): Convert to percentage
    • onDecimalPressed(): Add decimal point

ThemeProvider (extends ChangeNotifier)

  • Purpose: Manages app theme state
  • File: lib/theme_provider.dart
  • Key Methods:
    • toggleTheme(): Switch between light/dark mode
    • setThemeMode(): Set specific theme
  • Static Properties:
    • lightTheme: Light theme configuration
    • darkTheme: Dark theme configuration

HistoryManager (extends ChangeNotifier)

  • Purpose: Manages calculation history with persistent storage
  • File: lib/history_manager.dart
  • Key Methods:
    • loadHistory(): Load saved history from device storage (async)
    • addCalculation(): Add new calculation and save to storage (async)
    • clearHistory(): Remove all history and update storage (async)
    • removeAt(): Remove specific entry and save changes (async)
    • _saveHistory(): Private method to persist history to SharedPreferences
  • Properties:
    • history: List of CalculationHistory objects
    • isEmpty: Check if history is empty
    • count: Number of history entries
  • Storage Implementation:
    • Uses SharedPreferences for persistent local storage
    • Stores history as JSON string with key 'calculation_history'
    • Automatically saves after every modification (add/remove/clear)

CalculationHistory (Model Class)

  • Purpose: Represents a single calculation entry
  • File: lib/history_manager.dart
  • Properties:
    • calculation: The expression (e.g., "15 + 23")
    • result: The answer (e.g., "38")
    • timestamp: When it was calculated
  • Factory Methods:
    • fromCalculationString(): Parse calculation string into model
    • fromJson(): Deserialize from JSON map (for loading from storage)
  • Serialization Methods:
    • toJson(): Convert to JSON map (for saving to storage)

Persistent Storage Implementation

How History Persists on Device

The app uses SharedPreferences to store calculation history permanently on the user's device. Here's how it works:

1. Data Serialization

Each CalculationHistory object is converted to JSON format:

Map<String, dynamic> toJson() {
  return {
    'calculation': calculation,
    'result': result,
    'timestamp': timestamp.toIso8601String(),
  };
}

2. Saving to Device

When history changes (add/remove/clear), the entire history list is:

  1. Converted to a list of JSON maps
  2. Encoded as a JSON string
  3. Saved to SharedPreferences with key 'calculation_history'
Future<void> _saveHistory() async {
  final prefs = await SharedPreferences.getInstance();
  final jsonList = _history.map((item) => item.toJson()).toList();
  final jsonString = jsonEncode(jsonList);
  await prefs.setString(_historyKey, jsonString);
}

3. Loading from Device

When the app starts:

  1. SharedPreferences retrieves the JSON string
  2. JSON is decoded into a list of maps
  3. Each map is converted back to CalculationHistory objects
  4. The history list is populated
Future<void> loadHistory() async {
  final prefs = await SharedPreferences.getInstance();
  final historyJson = prefs.getString(_historyKey);
  
  if (historyJson != null && historyJson.isNotEmpty) {
    final decoded = jsonDecode(historyJson) as List<dynamic>;
    _history.addAll(
      decoded.map((item) => CalculationHistory.fromJson(item))
    );
    notifyListeners();
  }
}

4. Automatic Initialization

In main.dart, the HistoryManager is created and loads history immediately:

ChangeNotifierProvider(
  create: (_) {
    final historyManager = HistoryManager();
    historyManager.loadHistory(); // Loads saved history on app start
    return historyManager;
  },
)

5. Storage Locations

SharedPreferences stores data in platform-specific locations:

  • Android: XML file in app's data directory
  • iOS: NSUserDefaults
  • Web: Browser's LocalStorage
  • Windows/Linux/macOS: JSON file in app's data directory

Benefits of This Approach

  • Persistent: History survives app restarts and device reboots
  • Automatic: No manual save button needed - saves after every change
  • Cross-platform: Works on all Flutter platforms
  • Reliable: Uses native platform storage APIs
  • Efficient: Only saves when needed, loads once at startup

Custom App Icon Implementation

How the App Icon Was Changed

The app uses a custom launcher icon instead of the default Flutter logo, providing a professional branded appearance on the device home screen.

Implementation Process

1. Icon Asset Preparation

  • Created/obtained custom icon image: nextgenicon.jpeg
  • Placed in assets/icon/ directory
  • Recommended size: 1024x1024 pixels for best quality

2. Package Configuration Added flutter_launcher_icons to pubspec.yaml:

dev_dependencies:
  flutter_launcher_icons: ^0.13.1

3. Icon Configuration Added icon generation settings to pubspec.yaml:

flutter_launcher_icons:
  android: true
  ios: true
  image_path: "assets/icon/nextgenicon.jpeg"

4. Icon Generation Ran the icon generator command:

flutter pub get
dart run flutter_launcher_icons

This automatically generated all required icon sizes:

  • Android: mipmap-mdpi (48x48), hdpi (72x72), xhdpi (96x96), xxhdpi (144x144), xxxhdpi (192x192)
  • iOS: App icon sets for all required sizes

5. Build Integration The generated icons are automatically included when building the app:

flutter build apk --release  # For Android
flutter build ios --release   # For iOS

What Happens Behind the Scenes

  1. flutter_launcher_icons reads the source image from assets/icon/nextgenicon.jpeg
  2. Automatically resizes it to all required dimensions for each platform
  3. Places the generated icons in the correct platform-specific directories:
    • Android: android/app/src/main/res/mipmap-*/ic_launcher.png
    • iOS: ios/Runner/Assets.xcassets/AppIcon.appiconset/
  4. Updates platform configuration files to reference the new icons
  5. The custom icon appears on the device home screen after installation

Benefits

  • Professional branding with custom app identity
  • Automatic generation of all required sizes and formats
  • Cross-platform support (Android, iOS, and more)
  • No manual work resizing or placing icon files
  • Consistent appearance across all device resolutions

App Name Configuration

How the App Name Was Changed

The app displays as "NextGen Calculator" on the device instead of the default package name nextgen_calculator. This provides a professional, user-friendly name on the home screen and app drawer.

Implementation Process

The app name is configured separately for each platform in their respective manifest files:

1. Android Configuration

Edit android/app/src/main/AndroidManifest.xml:

<application
    android:label="NextGen Calculator"
    ...
>
  • The android:label attribute sets the app name displayed on Android devices
  • Changed from "nextgen_calculator" to "NextGen Calculator"
  • This name appears in the app drawer, home screen, and recent apps list

2. iOS Configuration

Edit ios/Runner/Info.plist:

<key>CFBundleDisplayName</key>
<string>NextGen Calculator</string>
<key>CFBundleName</key>
<string>NextGen Calculator</string>
  • CFBundleDisplayName: The user-visible name shown on the iOS home screen
  • CFBundleName: The bundle name used by the system
  • Changed both from "nextgen_calculator" to "NextGen Calculator"

3. Rebuild the App

After changing the manifest files, rebuild the app:

flutter build apk --release  # For Android
flutter build ios --release   # For iOS

Key Points

  • Package name vs Display name: The package name in pubspec.yaml (nextgen_calculator) remains unchanged and is used internally by Flutter and for app identification
  • Display name: The user-visible name in manifest files can be anything user-friendly
  • Platform-specific: Each platform has its own configuration file for the app name
  • Rebuild required: Changes only take effect after rebuilding and reinstalling the app

Benefits

  • Professional appearance with properly formatted app name
  • User-friendly with spaces and proper capitalization
  • Searchable in app stores and device search
  • Consistent branding across all platforms

Color Scheme

Light Theme

  • Background: Colors.white / #FFFFFF
  • Display Background: Color(0xFFF1F2F3)
  • Button Background: Color(0xFFF1F2F3)
  • Operation Buttons: Color(0xFFE8E9EB) with blue text
  • Equals Button: Color(0xFF4A90E2) (Blue)
  • Text: Colors.black87

Dark Theme

  • Background: Color(0xFF22252D)
  • Display Background: Color(0xFF292D36)
  • Number Buttons: Color(0xFF2C2F38)
  • Operation/Function Buttons: Color(0xFF3D4451)
  • Equals Button: Color(0xFF5AA9E6) (Bright Blue)
  • Text: Colors.white

Screen Descriptions

1. Calculator Screen (lib/home.dart)

  • Top Bar: Theme toggle and history navigation
  • Display Area: Shows current expression and result
  • Button Grid: 5x4 grid with numbers, operations, and functions
  • Buttons:
    • Numbers: 0-9
    • Operations: +, −, ×, ÷
    • Functions: C (Clear), +/− (Sign toggle), % (Percent), ⌫ (Backspace)
    • Equals: = (Calculate)

2. History Screen (lib/history_screen.dart)

  • AppBar: Back button, title, and "Clear All" button
  • Empty State: Shown when no calculations exist
  • History List: Scrollable list of past calculations
  • Each Entry Shows:
    • Calculation expression
    • Result
    • Relative timestamp (e.g., "2h ago", "Yesterday")
  • Interactions:
    • Swipe left to delete individual entry
    • Tap "Clear All" to delete all (with confirmation)

Dependencies

dependencies:
  flutter:
    sdk: flutter
  provider: ^6.1.2           # State management
  shared_preferences: ^2.2.2 # Local persistent storage

dev_dependencies:
  flutter_launcher_icons: ^0.13.1 # App icon generator

Why Provider?

  • Simple: Easy to understand and implement
  • Efficient: Only rebuilds widgets that listen to changes
  • Scalable: Works well for small to medium apps
  • Recommended: Official Flutter team recommendation

Why SharedPreferences?

  • Persistent: Data survives app restarts and device reboots
  • Simple API: Easy key-value storage for simple data structures
  • Cross-platform: Works on Android, iOS, Web, and Desktop
  • Lightweight: Perfect for storing small amounts of data like settings and history

Why flutter_launcher_icons?

  • Automated: Generates all required icon sizes automatically
  • Multi-platform: Creates icons for Android, iOS, and other platforms
  • Time-saving: No need to manually resize icons for different densities
  • Consistent: Ensures proper icon formatting across all devices

Getting Started

Prerequisites

  • Flutter SDK (latest stable version)
  • Dart SDK
  • Android Studio / VS Code with Flutter extensions

Installation

# Clone the repository
git clone <repository-url>

# Navigate to project directory
cd nextgen_calculator

# Install dependencies
flutter pub get

# Run the app
flutter run

Building for Production

# Android
flutter build apk --release

# iOS
flutter build ios --release

# Web
flutter build web

Project Structure

lib/
├── main.dart              # App entry point, provider setup
├── home.dart             # Main calculator screen
├── history_screen.dart   # Calculation history screen
├── calculator_logic.dart # Calculator computation logic
├── theme_provider.dart   # Theme management
└── history_manager.dart  # History management & model

Key Learning Points

Flutter Concepts Demonstrated

  1. State Management: Provider pattern with ChangeNotifier
  2. Persistent Storage: SharedPreferences for local data persistence
  3. Data Serialization: JSON encoding/decoding for storage
  4. Async Programming: Future-based async operations for storage I/O
  5. Asset Management: Custom app icons and resource bundling
  6. Build Automation: Automated icon generation with flutter_launcher_icons
  7. Platform Configuration: Android and iOS manifest files for app metadata
  8. Responsive Design: Flexible layouts with Expanded and Column/Row
  9. Theming: Dynamic theme switching with ThemeProvider
  10. Navigation: Screen transitions with Navigator
  11. Gestures: Dismissible for swipe-to-delete
  12. List Building: Efficient ListView.builder
  13. Dialogs: AlertDialog for confirmations
  14. Custom Widgets: Reusable button builder method
  15. Model Classes: Data encapsulation with CalculationHistory
  16. Separation of Concerns: UI, logic, and state management separated

Resources

About

A modern, feature-rich calculator application built with Flutter. This app includes a clean UI, dark/light mode support, calculation history, and smooth animations.

Resources

Stars

Watchers

Forks

Packages

No packages published