Skip to content

feat: UI layer implementation & real-time WebSocket architecture#4

Merged
Thixq merged 24 commits intomainfrom
dev/ui
Feb 8, 2026
Merged

feat: UI layer implementation & real-time WebSocket architecture#4
Thixq merged 24 commits intomainfrom
dev/ui

Conversation

@Thixq
Copy link
Copy Markdown
Owner

@Thixq Thixq commented Feb 8, 2026

🚀 UI Layer Implementation & Real-Time WebSocket Architecture

Summary

This PR introduces the complete UI layer (Home & Coin Detail screens) with a high-performance real-time data pipeline powered by WebSocket streams, background isolate parsing, and smart state management via Provider.


🏗️ Architecture Overview

┌──────────────┐     ┌──────────────┐     ┌───────────────────────┐
│   HomeView   │◄────│HomeViewModel │◄────│    MarketManager      │
│ CoinDetail   │     │  (Provider)  │     │  (REST + WebSocket)   │
└──────────────┘     └──────────────┘     └───────┬───────────────┘
                                                  │
                                    ┌─────────────┴─────────────┐
                                    │                           │
                              ┌─────▼──────┐          ┌────────▼────────┐
                              │ DioService │          │WebSocketService │
                              │ (Snapshot) │          │  (Real-time)    │
                              └────────────┘          └────────┬────────┘
                                                               │
                                                    ┌──────────▼──────────┐
                                                    │ IsolateParser       │
                                                    │ (Background JSON)   │
                                                    └─────────────────────┘

✨ Features & Changes

🖥️ UI Layer (New)

  • HomeView — Filterable coin list with real-time price updates, search bar with debounce, keyboard-aware scrolling
  • SmartCoinRow — Self-updating row widget that subscribes directly to MarketManager.getCoinStream() for its symbol, avoiding parent-level rebuilds
  • CoinDetailView — Scrollable detail page with price section, 24h stats grid, and volume cards
  • HomeViewModel — Provider-based state management with loading → loaded → disconnected → error lifecycle

⚡ Performance Optimizations

  • Isolate-based JSON parsingWebSocketIsolateParser processes incoming WebSocket messages on a background isolate with incremental char-by-char JSON scanning
  • Throttled UI updatesMarketManager batches price updates every 1000ms to avoid excessive widget rebuilds
  • Selective notificationsHomeViewModel.notifyListeners() only fires on snapshot emissions or state transitions, not on every price tick
  • RepaintBoundary on list items to isolate repaint regions
  • distinct() on coin streams using Equatable comparison

🔌 WebSocket & Connectivity

  • Constructor-injected parserWebSocketService now requires parser and manuellyRetry via constructor (removes runtime setParser())
  • Manual retry support — When manuellyRetry: true, auto-reconnect is disabled; user triggers reconnect explicitly
  • Socket status streamMarketManager.socketStatusStream exposes connection state (connected, disconnected, reconnecting)
  • Disconnect detectionHomeViewModel listens to socket status and shows a "Connection Lost" dialog with retry action
  • Heartbeat mechanism — 5s inactivity timeout assumes dead connection and triggers reconnect

🏛️ Dependency Injection

  • DependencyContainer — Centralized GetIt configuration with readOrCreate<T>() for lazy singleton registration
  • DependencyInstances — Clean API to access services (dioService, webSocketService<T>()) and managers (marketManager)

🧪 Tests

  • WebSocketIsolateParser tests — Comprehensive tests covering single object, array, large batch, and error scenarios
  • Updated WebSocketService tests — Adapted to constructor-based parser injection
  • Updated MarketManager tests — Mock regeneration and useIsolate parameter stubs

📁 Files Changed (19 files)

Category Files
New — Views home_view.dart, home_view_model.dart, smart_coin_row.dart, coin_detail_view.dart, coin_detail_view_model.dart, detail_card.dart, detail_grid.dart, price_section.dart
New — Services websocket_isolate_parser.dart
New — DI dependency_container.dart, dependency_instances.dart
Modified — Core market_manager.dart, web_socket_service.dart, main.dart
Modified — Tests market_manager_test.dart, market_manager_test.mocks.dart, web_socket_service_test.dart
New — Tests websocket_isolate_parser_test.dart
Config devtools_options.yaml

Stats: +1449 / -264 across 19 files


🔍 How to Test

  1. flutter pub get
  2. flutter run — Home screen loads coin list via REST snapshot, then live prices stream via WebSocket
  3. Search "BTC" or "TRY" in the search bar
  4. Tap any coin to open the detail page
  5. Kill network to observe "Connection Lost" dialog → tap Retry
  6. flutter test — All unit tests pass

Thixq added 24 commits February 7, 2026 23:53
- Add resizeToAvoidBottomInset: false to Scaffold
- Implement background isolate filtering with compute
- Add debounce (300ms) to search input
- Wrap list items in RepaintBoundary
…eParser

- Replace full jsonDecode with manual char-by-char scanner for large lists
- Enable chunked data processing directly from string stream
- Optimize memory usage by parsing list items individually
- Implement comprehensive test suite for `WebSocketIsolateParser`.
- Verify manual scanner logic for handling large JSON lists and chunking.
- Add robustness tests to ensure converter skips malformed JSON items without crashing.
- Validate correct handling of nested braces and string boundaries in the manual parser.
- Add `_isDisposed` flag to `CoinDetailViewModel` to prevent `notifyListeners` calls after the view model has been disposed.
- Update `CoinDetailView` to capture the ViewModel reference in `didChangeDependencies` for safe access during `dispose`.
- Ensure asynchronous operations in `init` check for `mounted` state before interacting with the context.
- Remove manual dispose call in View since Provider handles the lifecycle.
- Move CoinDetailViewModel provider from global MultiProvider to
  route-scoped ChangeNotifierProvider inside CoinDetailView
- Extract _PriceSection into widgets/price_section.dart (PriceSection)
- Extract _DetailGrid into widgets/detail_grid.dart (DetailGrid)
- Convert CoinDetailBody from StatefulWidget to StatelessWidget
  (setSymbol is already called in provider create, no lifecycle needed)
- Remove unused CoinDetailViewModel provider from main.dart
- Remove manual ViewModel dispose in HomeView (Provider handles lifecycle)
- Use CircularProgressIndicator.adaptive() for platform consistency
- Remove expensive .distinct() from market stream subscription
- Add null safety to ticker map comprehension
- Remove unnecessary List.from() copy in filter
- Fix leading space in SmartCoinRow subtitle text
- Fix incorrect doc comment referencing CoinDetailBody
- Use context.textTheme extension in PriceSection
- Translate detail grid card titles from Turkish to English
- Update AppBar title from 'Detay' to 'Detail'
- Clean up unused double-underscore builder params
…Isolate parameter\n\nAlso refactor getCoinStream to use changedTickers filter for O(1) lookup
…tion\n\n- Remove _tickerMap from HomeViewModel, delegate to MarketManager.getTicker()\n- Skip _applyFilter on price-only updates using changedTickers check\n- Update test delays to match 1000ms throttle duration
…anded with SingleChildScrollView\n- Add shrinkWrap to DetailGrid GridView for proper sizing
…ct() to getCoinStream using Equatable comparison\n- SmartCoinRow now listens directly to MarketManager.getCoinStream\n- HomeViewModel only calls notifyListeners on snapshot/state changes\n- Rows rebuild only when their specific coin data actually changes
…connect handling

- WebSocketService now requires parser and manuellyRetry via constructor instead of setParser()
- Added manual retry support to WebSocketService and MarketManager
- Exposed socketStatusStream from MarketManager for connection monitoring
- HomeViewModel listens to socket status for disconnected/reconnected states
- Added HomeViewState.disconnected to show connection lost dialog with retry
- Updated DependencyContainer to pass parser and manuellyRetry to WebSocketService
- Minor fixes: unawaited annotations, FormatException for unsupported message types
…ry changes

- Update web_socket_service_test to use constructor-based parser injection
- Update market_manager_test and regenerate mocks for new WebSocketService API
@Thixq Thixq merged commit 4120eee into main Feb 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant