Table of Contents
- Introduction to Dart Programming Fundamentals
- 1.1 Why Learn Dart for Flutter Development?
- 1.2 Overview of Chapter Objectives and Learning Outcomes
- 1.3 Setting Up Your Dart Environment: Step-by-Step Guide
- Dart Syntax, Variables, and Data Types
- 2.1 Basic Dart Syntax Rules: Getting Started with Hello World
- 2.2 Variables in Dart: Declaration, Initialization, and Naming Conventions
- 2.3 Primitive Data Types: Numbers, Strings, Booleans, and More
- 2.4 Advanced Data Types: Lists, Maps, Sets, and Runes
- 2.5 Type Inference with 'var', 'dynamic', and 'final/const'
- 2.6 Real-Life Examples: Managing User Profiles in a Social App
- 2.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
- Control Structures: Conditionals and Loops
- 3.1 Conditional Statements: if, if-else, else-if Ladders, and Ternary Operators
- 3.2 Switch-Case Statements for Multi-Way Branching
- 3.3 Loops: for, while, do-while, and for-in Iterations
- 3.4 Break, Continue, and Nested Loops
- 3.5 Real-Life Examples: Inventory Management System in an E-Commerce App
- 3.6 Best Practices, Exception Handling, Pros/Cons, and Alternatives
- Functions, Parameters, and Return Types
- 4.1 Defining and Calling Functions: Basics and Syntax
- 4.2 Parameters: Positional, Named, Optional, and Default Values
- 4.3 Return Types: Void, Single Values, and Multiple Returns via Tuples
- 4.4 Arrow Functions and Anonymous Functions
- 4.5 Higher-Order Functions and Closures
- 4.6 Real-Life Examples: Processing Orders in a Food Delivery App
- 4.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
- Object-Oriented Programming in Dart: Classes, Objects, and Inheritance
- 5.1 Classes and Objects: Constructors, Properties, and Methods
- 5.2 Inheritance: Single, Multi-Level, and Hierarchical
- 5.3 Overriding Methods and Using 'super'
- 5.4 Abstract Classes, Interfaces, and Mixins
- 5.5 Encapsulation, Polymorphism, and Abstraction in Practice
- 5.6 Real-Life Examples: Building a Vehicle Rental System
- 5.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
- Asynchronous Programming: Futures, Async, and Await
- 6.1 Understanding Synchronous vs. Asynchronous Code
- 6.2 Futures: Creating, Chaining, and Handling Errors
- 6.3 Async and Await Keywords for Readable Async Code
- 6.4 Streams for Continuous Data Flows
- 6.5 Isolates for Concurrency in Dart
- 6.6 Real-Life Examples: Fetching Weather Data in a Mobile App
- 6.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
- Practical Exercise: Building a Simple Dart Calculator Program
- 7.1 Step-by-Step Project Setup
- 7.2 Implementing Basic Operations: Addition, Subtraction, Multiplication, Division
- 7.3 Adding Advanced Features: Error Handling, User Input Validation, and History Logging
- 7.4 Real-Life Integration: Extending to a Budget Tracker App
- 7.5 Testing and Debugging Your Calculator
- Conclusion and Next Steps
- 8.1 Recap of Key Learning Outcomes
- 8.2 Tips for Advancing Your Dart Skills
- 8.3 Preview of Upcoming Chapters in the Master Flutter & Dart Course
1. Introduction to Dart Programming Fundamentals
1.1 Why Learn Dart for Flutter Development?
Dart is the programming language powering Flutter, Google's UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase. As a beginner stepping into mobile app development, understanding Dart is crucial because it forms the backbone of Flutter widgets, state management, and app logic. Imagine building a house without knowing how to use bricks—Dart is those bricks for Flutter.
In real life, Dart's design makes it ideal for apps that need high performance and smooth user experiences, like banking apps where quick calculations and responsive interfaces are key. For instance, apps like Alibaba and Hamilton use Flutter with Dart to handle millions of users seamlessly. Learning Dart not only equips you for Flutter but also introduces concepts transferable to other languages like JavaScript or Java.
From a beginner's perspective, Dart is user-friendly with its clean syntax, which resembles JavaScript but with stronger typing for fewer bugs. For advanced users, its just-in-time (JIT) compilation during development and ahead-of-time (AOT) for production ensure fast execution. Pros of Dart include strong null safety (introduced in Dart 2.12), which prevents null pointer exceptions—a common pain in other languages. Cons? It's less widespread outside Flutter ecosystems, so community resources might be Flutter-focused. Alternatives like Kotlin for Android or Swift for iOS exist, but Dart's cross-platform nature wins for multi-platform apps.
Best practice: Always enable null safety in your projects with --dart-define=environment=null-safety to catch errors early.
1.2 Overview of Chapter Objectives and Learning Outcomes
This chapter dives deep into Dart's core concepts to build a solid foundation. We'll cover syntax basics, control flows, functions, OOP, and async programming—everything step-by-step with code snippets.
By the end, you'll be able to:
- Write basic Dart programs with proper syntax.
- Understand asynchronous programming for app responsiveness.
- Apply these in real-world scenarios, like data processing in apps.
We'll use real-life examples, such as managing e-commerce inventories or fetching API data, to make learning engaging and practical.
1.3 Setting Up Your Dart Environment: Step-by-Step Guide
To start coding in Dart, follow this easy setup:
- Download Dart SDK: Visit the official Dart website (dart.dev) and download the SDK for your OS (Windows, macOS, Linux). It's free and lightweight.
- Install via Package Manager:
- On macOS: Use Homebrew with brew tap dart-lang/dart then brew install dart.
- On Windows: Use Chocolatey with choco install dart-sdk.
- On Linux: sudo apt-get install dart.
- Verify Installation: Open a terminal and run dart --version. You should see something like "Dart SDK version: 3.1.0 (stable)".
- Set Up an IDE: Use VS Code with the Dart extension for syntax highlighting and debugging. Alternatively, Android Studio or IntelliJ for Flutter integration.
- Create Your First Project: Run dart create my_first_app to generate a sample project. Navigate to it and run dart run to execute.
Real-life tip: In a team setting, use pub (Dart's package manager) to manage dependencies. For example, dart pub add http for network requests.
Exception handling during setup: If dart command not found, add the SDK's bin folder to your PATH environment variable. Pros of this setup: Quick and cross-platform. Cons: Manual updates needed. Alternative: Use Flutter SDK, which bundles Dart.
Now, let's jump into the core topics.
2. Dart Syntax, Variables, and Data Types
Dart's syntax is straightforward, making it accessible for beginners while powerful for advanced use. We'll start with basics and build up to complex data handling.
2.1 Basic Dart Syntax Rules: Getting Started with Hello World
Dart programs start with a main() function, the entry point. Comments use // for single-line or /* */ for multi-line. Semicolons end statements.
Here's a beginner's Hello World:
void main() {
print('Hello, World!'); // Outputs to console
}
Run this with dart run hello.dart. In real life, this is like printing a welcome message in a chat app.
Advanced: Use string interpolation for dynamic output: print('Hello, $name!');.
Best practice: Use consistent indentation (2 spaces) for readability.
2.2 Variables in Dart: Declaration, Initialization, and Naming Conventions
Variables store data. Declare with type or use inference.
Basic example:
String name = 'Alice'; // Explicit type
var age = 25; // Inferred as int
Naming: Use camelCase for variables, e.g., userScore. Avoid starting with numbers.
Real-life: In a fitness app, double weight = 70.5; tracks user weight.
Exception handling: Dart throws errors for uninitialized variables in null-safe mode. Use late for lazy initialization: late String description;.
Pros: Strong typing reduces runtime errors. Cons: More verbose than dynamic languages like Python. Alternative: JavaScript's let for similar inference.
2.3 Primitive Data Types: Numbers, Strings, Booleans, and More
Dart primitives include:
- int: Whole numbers, e.g., int score = 100;.
- double: Floating points, e.g., double pi = 3.14;.
- String: Text, supports multi-line with triple quotes: String message = '''Hello\nWorld''';.
- bool: True/false, e.g., bool isActive = true;.
- num: Super type for int/double.
Basic operations: int sum = 5 + 3;, string concatenation String fullName = 'John' + ' Doe';.
Real-life example: In a banking app, calculate interest:
double principal = 1000.0;
double rate = 0.05;
double interest = principal * rate;
print('Interest: $interest'); // Output: Interest: 50.0
Advanced: Use BigInt for large numbers: BigInt largeNum = BigInt.from(12345678901234567890);.
Best practice: Use const for compile-time constants: const double gravity = 9.8;.
Exception: Division by zero throws IntegerDivisionByZeroException. Handle with try-catch:
try {
int result = 10 ~/ 0;
} catch (e) {
print('Error: $e');
}
Pros: Efficient memory use. Cons: No unsigned types. Alternatives: C++ for low-level control.
2.4 Advanced Data Types: Lists, Maps, Sets, and Runes
- List: Ordered collection, e.g., List<int> numbers = [1, 2, 3];. Add with numbers.add(4);.
- Map: Key-value pairs, e.g., Map<String, int> scores = {'Alice': 90, 'Bob': 85};. Access: scores['Alice'].
- Set: Unique items, e.g., Set<String> fruits = {'apple', 'banana'};.
- Runes: For Unicode, e.g., handling emojis: Runes emoji = Runes('\u{1F600}');.
Real-life: In a shopping app, use List for cart items:
List<String> cart = ['Milk', 'Bread'];
cart.add('Eggs');
print(cart); // [Milk, Bread, Eggs]
Advanced: Generics for type safety: List<Map<String, dynamic>> users = [{'name': 'Alice'}];.
Best practice: Use spread operators for combining: List<int> combined = [...list1, ...list2];.
Exception: Index out of range: Use try { list[10]; } catch (e) { print('Out of bounds'); }.
Pros: Built-in collections are versatile. Cons: No built-in queues (use List). Alternatives: Python lists for similar ease.
2.5 Type Inference with 'var', 'dynamic', and 'final/const'
- var: Infers type at compile-time, can't change type.
- dynamic: Can change type, but risky.
- final: Assigned once, runtime.
- const: Compile-time constant.
Example:
var name = 'Dart'; // Inferred String
dynamic flexible = 10; flexible = 'Now string';
final int age = 30; // Can't reassign
const double pi = 3.14;
Real-life: In config files, use const for app constants.
Best practice: Prefer var over explicit types for brevity, but use types for clarity in APIs.
Exception: Type mismatch with dynamic can cause runtime errors—avoid unless necessary.
Pros: Flexibility with safety. Cons: Dynamic can lead to bugs. Alternatives: TypeScript's 'any'.
2.6 Real-Life Examples: Managing User Profiles in a Social App
Imagine building a social media app like Instagram. Users have profiles with name, age, friends list, and bio.
Code example (basic to advanced):
Basic profile:
String username = 'user123';
int followers = 500;
bool isVerified = true;
List<String> friends = ['friend1', 'friend2'];
Map<String, dynamic> profile = {
'username': username,
'followers': followers,
'isVerified': isVerified,
'friends': friends
};
print(profile);
Advanced: Add validation:
void createProfile(String name, int age) {
if (age < 13) {
throw Exception('Age must be at least 13');
}
var profile = {'name': name, 'age': age};
print('Profile created: $profile');
}
try {
createProfile('Kid', 12);
} catch (e) {
print('Error: $e');
}
In real scenarios, this handles user data storage, ensuring compliance like age restrictions for social apps. Extend with JSON serialization for API integration.
Pros: Data types make profiles efficient. Cons: Manual validation needed. Best practice: Use classes for complex profiles (covered later).
2.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
Best practices:
- Use meaningful variable names.
- Prefer immutable data with final/const for performance.
- Enable strict mode for types.
Exception handling: Always wrap risky code in try-catch-finally. Custom exceptions: class InvalidAgeException implements Exception {}.
Pros of Dart data types: Null safety, performance. Cons: Steeper learning for dynamic lang users. Alternatives: Java for similar typing, Python for simplicity.
This section alone provides a strong base—now onto control structures.
3. Control Structures: Conditionals and Loops
Control structures direct code flow, essential for decision-making and repetition in apps.
3.1 Conditional Statements: if, if-else, else-if Ladders, and Ternary Operators
Basic if:
int age = 18;
if (age >= 18) {
print('Adult');
}
If-else:
if (age >= 18) {
print('Adult');
} else {
print('Minor');
}
Else-if ladder for grades:
int score = 85;
if (score >= 90) {
print('A');
} else if (score >= 80) {
print('B');
} else {
print('C');
}
Ternary: String status = age >= 18 ? 'Adult' : 'Minor';.
Real-life: In a login system, check credentials.
Advanced: Combine with logical operators: if (age >= 18 && isCitizen) { print('Eligible to vote'); }.
Best practice: Avoid deep nesting; use early returns.
3.2 Switch-Case Statements for Multi-Way Branching
For discrete values:
String day = 'Monday';
switch (day) {
case 'Monday':
print('Start of week');
break;
case 'Friday':
print('Weekend ahead');
break;
default:
print('Midweek');
}
Real-life: Menu selection in a game app.
Advanced: Switch on enums: enum Color { red, blue }; switch(color) {...}.
Pros: Cleaner than long if-else. Cons: Limited to equality checks.
3.3 Loops: for, while, do-while, and for-in Iterations
For loop:
for (int i = 0; i < 5; i++) {
print(i);
}
While:
int count = 0;
while (count < 5) {
print(count);
count++;
}
Do-while (runs at least once):
int num = 5;
do {
print(num);
num--;
} while (num > 0);
For-in for collections:
List<String> fruits = ['apple', 'banana'];
for (var fruit in fruits) {
print(fruit);
}
Real-life: Processing transaction logs in a finance app.
Advanced: Infinite loops with break: while (true) { if (condition) break; }.
Best practice: Use for-in for readability with iterables.
3.4 Break, Continue, and Nested Loops
Break exits loop, continue skips iteration.
Nested example:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) continue;
print('$i-$j');
}
}
Real-life: Searching 2D grids in puzzle games.
Exception: Avoid unlabeled breaks in nested loops; use labels: outer: for (...) { break outer; }.
3.5 Real-Life Examples: Inventory Management System in an E-Commerce App
In an online store like Amazon, manage stock with conditionals and loops.
Basic: Check stock:
int stock = 10;
if (stock > 0) {
print('Available');
} else {
print('Out of stock');
}
Advanced: Loop through items and update:
List<Map<String, dynamic>> inventory = [
{'item': 'Shirt', 'stock': 5},
{'item': 'Pants', 'stock': 0}
];
for (var item in inventory) {
String status = item['stock'] > 0 ? 'In stock' : 'Sold out';
print('${item['item']}: $status');
if (item['stock'] == 0) {
// Notify supplier
print('Restock ${item['item']}');
}
}
Handle exceptions like invalid stock:
try {
if (stock < 0) throw Exception('Negative stock invalid');
} catch (e) {
print('Error: $e');
}
This simulates real e-commerce logic, ensuring app responsiveness. Extend with databases for persistence.
Pros: Efficient for batch processing. Cons: Loops can be slow for large data—use streams for big datasets.
3.6 Best Practices, Exception Handling, Pros/Cons, and Alternatives
Best practices:
- Use switch for enums.
- Avoid mutable state in loops.
- Profile loops for performance.
Exception handling: Catch loop errors like index errors.
Pros: Flexible control. Cons: Potential infinite loops. Alternatives: Recursion for tree traversals, but watch stack overflow.
4. Functions, Parameters, and Return Types
Functions encapsulate reusable code, key for modular apps.
4.1 Defining and Calling Functions: Basics and Syntax
Basic:
void greet() {
print('Hello!');
}
greet();
With return:
int add(int a, int b) {
return a + b;
}
print(add(2, 3)); // 5
Real-life: Utility functions in utilities class.
4.2 Parameters: Positional, Named, Optional, and Default Values
Positional: add(int a, int b)
Named: void info({String? name, int? age}) { ... } Call: info(name: 'Alice', age: 25);
Optional: void opt([String name = 'Guest']) { ... }
Real-life: API calls with optional params.
Advanced: Required named: {required String name}.
4.3 Return Types: Void, Single Values, and Multiple Returns via Tuples
Void for no return. Multiple: Use records (Dart 3+): (int, String) getInfo() => (42, 'Answer');
Example: var (num, text) = getInfo();
4.4 Arrow Functions and Anonymous Functions
Arrow: int square(int x) => x * x;
Anonymous: var anon = (int x) => x * 2;
Real-life: Callbacks in event handlers.
4.5 Higher-Order Functions and Closures
Higher-order: Functions taking/returning functions.
Example: void execute(Function op) { op(); }
Closure: Captures variables.
Function counter() {
int count = 0;
return () => count++;
}
var c = counter();
print(c()); // 0
print(c()); // 1
Advanced: In state management.
4.6 Real-Life Examples: Processing Orders in a Food Delivery App
In apps like Uber Eats, process orders.
Basic function:
double calculateTotal(List<double> prices) {
double total = 0;
for (var price in prices) {
total += price;
}
return total;
}
print(calculateTotal([10.5, 5.0])); // 15.5
Advanced with params and exceptions:
double calcOrder({required List<double> items, double discount = 0.0}) {
if (items.isEmpty) throw Exception('No items');
double sum = items.reduce((a, b) => a + b);
return sum - (sum * discount);
}
try {
print(calcOrder(items: [10, 20], discount: 0.1)); // 27.0
} catch (e) {
print('Error: $e');
}
This handles real orders, adding taxes or fees. Pros: Reusable. Cons: Overuse leads to complexity.
4.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
Best practices:
- Keep functions short (<50 lines).
- Use named params for clarity.
Exception: Throw specific exceptions.
Pros: Modularity. Cons: Overhead in deep calls. Alternatives: Lambdas in Kotlin.
5. Object-Oriented Programming in Dart: Classes, Objects, and Inheritance
OOP models real-world entities.
5.1 Classes and Objects: Constructors, Properties, and Methods
Class:
class Person {
String name;
int age;
Person(this.name, this.age); // Constructor
void introduce() {
print('Hi, I\'m $name, $age years old.');
}
}
var p = Person('Alice', 30);
p.introduce();
Properties: Getters/setters.
class Rectangle {
double _width;
double get width => _width;
set width(double value) {
if (value < 0) throw Exception('Negative width');
_width = value;
}
}
Real-life: User models in apps.
5.2 Inheritance: Single, Multi-Level, and Hierarchical
Single:
class Animal {
void eat() { print('Eating'); }
}
class Dog extends Animal {
void bark() { print('Woof'); }
}
var d = Dog();
d.eat();
d.bark();
Multi-level: Class Puppy extends Dog.
Hierarchical: Multiple subclasses from Animal.
5.3 Overriding Methods and Using 'super'
class Cat extends Animal {
@override
void eat() {
super.eat();
print('Meow while eating');
}
}
5.4 Abstract Classes, Interfaces, and Mixins
Abstract:
abstract class Shape {
double area();
}
Interface: Classes act as interfaces.
Mixin: mixin Flyable { void fly() { ... } } class Bird with Flyable {}
Advanced: Multiple mixins.
5.5 Encapsulation, Polymorphism, and Abstraction in Practice
Encapsulation: Private members with _.
Polymorphism: Treat subclasses as super.
Abstraction: Hide details.
5.6 Real-Life Examples: Building a Vehicle Rental System
For a rental app like Zipcar.
Basic class:
class Vehicle {
String model;
double pricePerDay;
Vehicle(this.model, this.pricePerDay);
double calculateRental(int days) {
return pricePerDay * days;
}
}
Inheritance:
class Car extends Vehicle {
int seats;
Car(String model, double price, this.seats) : super(model, price);
@override
double calculateRental(int days) {
double base = super.calculateRental(days);
return base + (seats > 4 ? 10 : 0); // Extra for larger cars
}
}
var sedan = Car('Toyota', 50.0, 5);
print(sedan.calculateRental(3)); // 180.0
Advanced with exceptions:
class Bike extends Vehicle {
@override
double calculateRental(int days) {
if (days <= 0) throw Exception('Invalid days');
return super.calculateRental(days) * 0.8; // Discount
}
}
try {
var bike = Bike('Honda', 20.0);
print(bike.calculateRental(0));
} catch (e) {
print('Error: $e');
}
Mixin for electric:
mixin Electric {
void charge() { print('Charging'); }
}
class ElectricCar extends Car with Electric {
ElectricCar(String model, double price, int seats) : super(model, price, seats);
}
var eCar = ElectricCar('Tesla', 100.0, 4);
eCar.charge();
This models real rental logic, calculating costs with features. Pros: Scalable. Cons: Inheritance abuse leads to fragility.
5.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
Best practices:
- Favor composition over inheritance.
- Use abstract for templates.
Exception: Validate in constructors.
Pros: Reusability. Cons: Tight coupling. Alternatives: Functional programming in Haskell.
6. Asynchronous Programming: Futures, Async, and Await
Async keeps apps responsive during I/O.
6.1 Understanding Synchronous vs. Asynchronous Code
Sync blocks: print('Wait'); sleep(2); print('Done');
Async doesn't block.
6.2 Futures: Creating, Chaining, and Handling Errors
Future:
Future<String> fetchData() {
return Future.delayed(Duration(seconds: 2), () => 'Data fetched');
}
fetchData().then((data) => print(data));
Chaining: .then((v) => anotherFuture(v))
Error: .catchError((e) => print(e));
6.3 Async and Await Keywords for Readable Async Code
Future<void> main() async {
try {
String data = await fetchData();
print(data);
} catch (e) {
print('Error: $e');
}
}
Real-life: API calls.
6.4 Streams for Continuous Data Flows
Stream:
Stream<int> countStream() async* {
for (int i = 1; i <= 3; i++) {
yield i;
}
}
countStream().listen((num) => print(num));
For real-time updates.
6.5 Isolates for Concurrency in Dart
Isolates: Separate memory threads.
import 'dart:isolate';
void isolateFunc(SendPort port) {
port.send('From isolate');
}
void main() async {
ReceivePort receive = ReceivePort();
Isolate.spawn(isolateFunc, receive.sendPort);
print(await receive.first);
}
Advanced for heavy computations.
6.6 Real-Life Examples: Fetching Weather Data in a Mobile App
In a weather app like AccuWeather.
Basic future:
Future<Map<String, dynamic>> getWeather() async {
// Simulate API
await Future.delayed(Duration(seconds: 1));
return {'temp': 25, 'condition': 'Sunny'};
}
Usage:
Future<void> displayWeather() async {
try {
var weather = await getWeather();
print('Temperature: ${weather['temp']}°C, ${weather['condition']}');
} catch (e) {
print('Failed to fetch: $e');
}
}
displayWeather();
Advanced with stream for live updates:
Stream<Map<String, dynamic>> weatherStream() async* {
while (true) {
yield await getWeather();
await Future.delayed(Duration(minutes: 5));
}
}
weatherStream().listen((data) => print(data));
Handle exceptions like network errors: Throw Exception('No internet').
This ensures the app doesn't freeze during fetches. Pros: Responsive UIs. Cons: Complexity in error propagation. Best practice: Use try-await-catch.
6.7 Best Practices, Exception Handling, Pros/Cons, and Alternatives
Best practices:
- Await in async functions.
- Use Completer for custom futures.
Exception: Propagate with rethrow.
Pros: Modern async model. Cons: Callback hell without await. Alternatives: Promises in JS.
7. Practical Exercise: Building a Simple Dart Calculator Program
Apply all concepts in a calculator.
7.1 Step-by-Step Project Setup
- Create file calculator.dart.
- Import dart:io for input.
7.2 Implementing Basic Operations: Addition, Subtraction, Multiplication, Division
Class:
class Calculator {
double add(double a, double b) => a + b;
double subtract(double a, double b) => a - b;
double multiply(double a, double b) => a * b;
double divide(double a, double b) {
if (b == 0) throw Exception('Division by zero');
return a / b;
}
}
7.3 Adding Advanced Features: Error Handling, User Input Validation, and History Logging
Main with input:
import 'dart:io';
void main() {
var calc = Calculator();
List<String> history = [];
while (true) {
print('Enter operation (add/sub/mul/div/exit):');
String op = stdin.readLineSync()!.trim().toLowerCase();
if (op == 'exit') break;
print('Enter first number:');
double a = double.parse(stdin.readLineSync()!);
print('Enter second number:');
double b = double.parse(stdin.readLineSync()!);
try {
double result;
switch (op) {
case 'add':
result = calc.add(a, b);
break;
case 'sub':
result = calc.subtract(a, b);
break;
case 'mul':
result = calc.multiply(a, b);
break;
case 'div':
result = calc.divide(a, b);
break;
default:
throw Exception('Invalid operation');
}
history.add('$op $a $b = $result');
print('Result: $result');
} catch (e) {
print('Error: $e');
}
print('History:');
for (var entry in history) {
print(entry);
}
}
}
Validation: Use try-parse for inputs.
7.4 Real-Life Integration: Extending to a Budget Tracker App
Extend calculator for budget: Add functions for expense tracking, using maps for categories.
Example: Map<String, double> expenses = {}; Add via functions.
This turns it into a practical tool for personal finance apps.
7.5 Testing and Debugging Your Calculator
Test cases: Input 10 + 5 = 15, divide by 0 error.
Debug: Use print or IDE breakpoints.
Pros: Hands-on learning. Cons: Console-only; integrate with Flutter for UI.
No comments:
Post a Comment
Thanks for your valuable comment...........
Md. Mominul Islam