No description
| _backup | ||
| app | ||
| dist | ||
| lib | ||
| tab-tracker-extension | ||
| test | ||
| web | ||
| .gitignore | ||
| .metadata | ||
| analysis_options.yaml | ||
| Dockerfile | ||
| nginx.conf | ||
| package.sh | ||
| pubspec.lock | ||
| pubspec.yaml | ||
| README.md | ||
| requirements.txt | ||
| setup-extension.sh | ||
| setup.sh | ||
browser_tab_manager
Getting Started
🧠 Browser Tab Manager - Complete Code Study Guide
📋 Table of Contents
- App Architecture Overview
- Entry Point - main.dart
- Data Layer - Models
- Service Layer
- UI Layer - Screens
- Component Layer - Widgets
- Utilities & Constants
- Data Flow & State Management
- Browser API Integration
- Extension Communication
🏗️ App Architecture Overview
┌─────────────────────────────────────────┐
│ main.dart │
│ (App Entry Point) │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ TabManagerHome │
│ (Main Screen State) │
└─────┬─────────────────────────────┬─────┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│ Services │ │ Widgets │
│ Layer │ │ Layer │
└─────┬─────┘ └─────┬─────┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│ Models │ │ Utils & │
│ Layer │ │ Constants │
└───────────┘ └───────────┘
Key Concepts:
- Separation of Concerns: Each layer has specific responsibilities
- Unidirectional Data Flow: Data flows down, events flow up
- State Management: Centralized in TabManagerHome using setState()
- Service Communication: Browser APIs and Extension messaging
🚀 Entry Point - main.dart
void main() {
runApp(const BrowserTabManagerApp());
}
WHAT HAPPENS HERE:
main()is the entry point of every Dart/Flutter apprunApp()tells Flutter to start the app with our root widget- Creates the widget tree and starts the rendering engine
class BrowserTabManagerApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Browser Tab Manager',
theme: ThemeData(...),
home: const TabManagerHome(),
);
}
}
BREAKDOWN:
StatelessWidget: Never changes its appearance based on internal stateMaterialApp: Root widget that provides Material Design themingtheme&darkTheme: Define app-wide visual stylinghome: The first screen users see (TabManagerHome)
📊 Data Layer - Models
TabData Model (models/tab_data.dart)
class TabData {
String id; // Unique identifier
String title; // Display name
String url; // Web address
String favicon; // Icon URL
DateTime lastAccessed; // When last used
bool isPinned; // Pinned status
String type; // 'tab', 'bookmark', or 'history'
int? visitCount; // Number of visits (nullable)
String? folder; // Bookmark folder (nullable)
}
PURPOSE:
- Data Structure: Represents all types of browser items uniformly
- Type Safety: Dart ensures correct data types
- Nullable Fields:
?means the field can be null
Factory Constructor:
factory TabData.fromJson(Map<String, dynamic> json) => TabData(
id: json['id'].toString(),
title: json['title'] ?? 'Untitled', // ?? means "if null, use default"
// ... more fields
);
WHAT IT DOES:
- Converts JSON data from browser APIs into TabData objects
- Handles missing/null values gracefully with defaults
- Standardizes different API response formats
🔧 Service Layer
BrowserApiService (services/browser_api_service.dart)
class BrowserApiService {
Future<List<TabData>> getTabs() async {
final result = await _callBrowserAPI('getTabs');
// Convert raw data to TabData objects
}
}
KEY CONCEPTS:
async/await: Handles asynchronous operationsFuture<T>: Represents a value that will be available later- Abstraction: Hides complex browser API details
ExtensionService (services/extension_service.dart)
void setupListener() {
html.window.onMessage.listen((event) => {
// Handle messages from browser extension
});
}
PURPOSE:
- Communication Bridge: Between web app and browser extension
- Event-Driven: Responds to messages from extension
- Callback Pattern: Uses function pointers for responses
🎨 UI Layer - Screens
TabManagerHome (screens/tab_manager_home.dart)
This is the BRAIN of the application - it manages all state and coordinates everything.
State Variables:
class _TabManagerHomeState extends State<TabManagerHome> {
List<TabData> allItems = []; // ALL data from browser
List<TabData> filteredItems = []; // DISPLAYED data (after search/filter)
bool isGridView = true; // View mode toggle
String sortBy = 'recent'; // Current sort method
String filterType = 'all'; // Current filter
bool isLoading = true; // Loading spinner state
bool extensionMode = false; // Extension tracking mode
}
Key Methods:
initState() - Runs when widget is created:
@override
void initState() {
super.initState(); // Call parent setup
_setupExtensionService(); // Start listening for extension
_loadAllData(); // Load browser data
searchController.addListener(_filterItems); // Watch search input
}
setState() - The magic that updates UI:
setState(() {
allItems = newData; // Change state
_filterItems(); // Update filtered view
});
// Flutter automatically rebuilds UI after setState finishes
🧩 Component Layer - Widgets
ItemCard (widgets/item_card.dart)
STATELESS WIDGET - Displays data, doesn't manage state:
class ItemCard extends StatelessWidget {
final TabData item; // Data from parent
final VoidCallback onTap; // Function to call when tapped
@override
Widget build(BuildContext context) {
return Card(
child: InkWell(
onTap: onTap, // Call parent's function
child: // ... UI layout
),
);
}
}
DATA FLOW:
- Parent passes data and callback functions
- Widget displays the data
- User interaction calls parent's functions
- Parent updates state and rebuilds widget with new data
SearchBar (widgets/search_bar.dart)
TextField(
controller: controller, // Links to parent's TextEditingController
onChanged: (text) => { // Calls parent when text changes
// Parent handles the search logic
},
)
🛠️ Utilities & Constants
Helpers (utils/helpers.dart)
static List<TabData> filterItems(
List<TabData> items,
String query,
String filterType,
) {
return items.where((item) => {
// Filter logic here
}).toList();
}
PURPOSE:
- Pure Functions: Same input always produces same output
- Reusable Logic: Can be used anywhere in the app
- Testing: Easy to unit test
Constants (constants/app_constants.dart)
class AppConstants {
static const Color primaryColor = Color(0xFF0175C2);
static const String extensionSource = 'tab-tracker-extension';
}
BENEFITS:
- Single Source of Truth: Change value in one place
- Type Safety: Compile-time checking
- Maintainability: Easy to update app-wide settings
🔄 Data Flow & State Management
Complete Data Flow Cycle:
1. User Action (tap, type, click)
↓
2. Widget calls parent function
↓
3. Parent updates state with setState()
↓
4. Flutter rebuilds widget tree
↓
5. UI reflects new state
Example: Search Flow
// 1. User types in search bar
SearchBar(onChanged: (text) => {
// 2. SearchBar calls parent's function
_filterItems();
});
// 3. Parent filters data and updates state
void _filterItems() {
setState(() => {
filteredItems = // ... filter logic
});
// 4. Flutter rebuilds UI with new filteredItems
}
🌐 Browser API Integration
How Browser APIs Work:
Future<String?> _callBrowserAPI(String method, [List<dynamic>? args]) async {
// 1. Check if browser API exists
if (!js_util.hasProperty(html.window, 'BrowserAPI')) {
return null; // Development mode
}
// 2. Get the API object from browser
final browserAPI = js_util.getProperty(html.window, 'BrowserAPI');
// 3. Call the specific method
final result = await js_util.promiseToFuture(
js_util.callMethod(function, 'call', [browserAPI, ...args])
);
return json.encode(result); // 4. Return as JSON string
}
STEPS:
- Check Availability: Is the browser API injected?
- Get Reference: Access the API object
- Call Method: Execute with parameters
- Handle Response: Convert to usable format
📡 Extension Communication
Message Passing System:
// SENDING to extension:
html.window.postMessage({
'source': 'tab-tracker-webapp',
'action': 'startTracking'
}, '*');
// RECEIVING from extension:
html.window.onMessage.listen((event) => {
final data = event.data;
if (data['source'] == 'tab-tracker-extension') {
_handleExtensionMessage(data);
}
});
COMMUNICATION FLOW:
Web App ←→ Browser Window ←→ Extension
Message Types:
startTracking: Begin tab monitoringstopTracking: Stop tab monitoringupdateTabs: Extension sends current tabsgetStatus: Request current state
🎯 Key Learning Points
1. State Management Pattern
- State lives in parent components
- Children receive data and callback functions
- setState() triggers UI rebuilds
2. Async Programming
async/awaitfor non-blocking operationsFuture<T>represents eventual values- Error handling with try/catch
3. Widget Communication
- Parent-to-child: Pass data via constructor
- Child-to-parent: Pass callback functions
- Sibling-to-sibling: Through common parent
4. Service Layer Benefits
- Separates business logic from UI
- Makes testing easier
- Provides clean abstractions
5. Browser Integration
- JavaScript interop for browser APIs
- Message passing for extension communication
- Graceful degradation for development mode
🔍 Next Steps for Deep Learning
- Trace a complete user action from UI tap to state update
- Follow data transformation from browser API to UI display
- Understand lifecycle methods and when they're called
- Practice modifying one small feature at a time
- Add logging to see the flow in action
This architecture provides a solid foundation for building complex, maintainable Flutter web applications! 🚀