# 📐 اطلاعات Layout و Responsive که باید از Flutter به WebView ارسال شود ## 🎯 خلاصه اجرایی این سند تمام اطلاعاتی که Flutter باید به WebView ارسال کند تا صفحه به‌درستی نمایش داده شود را مستند می‌کند. --- ## 📊 ۱. Responsive Breakpoints ### تعریف در Flutter: ```dart // lib/app/application.dart (خط 123-126) ResponsiveBreakpoints.builder( breakpoints: const [ Breakpoint(start: 0, end: 800, name: MOBILE), Breakpoint(start: 800, end: 1600, name: TABLET), ], ) ``` ### استفاده در کد: ```dart // lib/core/util/extensions/context_extensions.dart bool get isMobile => breakpoints.isMobile; // width < 800 bool get isTablet => breakpoints.isTablet; // width >= 800 bool get isDesktop => breakpoints.isDesktop; // width >= 1600 (غیرفعال) double get screenWidth => breakpoints.screenWidth; ``` ### ✅ باید به WebView ارسال شود: ```json { "layout": { "breakpoint": "mobile" | "tablet" | "desktop", "screenWidth": 375, "isMobile": true, "isTablet": false } } ``` ### 📱 تاثیر در UI: | بخش | Mobile (< 800px) | Tablet (>= 800px) | |-----|-----------------|-------------------| | **Padding افقی** | `16px` | `64px` | | **Search Bar** | عرض کامل | عرض محدود با padding 64px | | **Chat List** | Single column | Single column با padding 64px | | **TabBar** | Centered، غیر اسکرول | Left-aligned، scrollable | | **Calendar Grid** | `width / 120` columns | `width / 120` columns | | **Chat TextField** | padding: 16px | padding: 64px horizontal | --- ## 📏 ۲. Safe Area & System Insets ### تعریف در Flutter: ```dart // lib/core/util/extensions/context_extensions.dart MediaQueryData get mq => MediaQuery.of(context); EdgeInsets get insets => mq.viewInsets; // Keyboard, etc EdgeInsets get padding => mq.viewPadding; // Status bar, notch, home indicator double get navigationBarHeight => max(insets.bottom, padding.bottom); ``` ### ✅ باید به WebView ارسال شود: ```json { "safeArea": { "top": 44, // Status bar / notch "bottom": 34, // Home indicator (iPhone X+) "left": 0, "right": 0 }, "viewInsets": { "top": 0, "bottom": 336, // Keyboard height (تغییر می‌کند) "left": 0, "right": 0 }, "navigationBarHeight": 34 // max(viewInsets.bottom, safeArea.bottom) } ``` ### 📱 تاثیر در UI: ```dart // مثال‌های استفاده در کد: // 1. Chat Page - Floating Action Button bottom: 16 + context.padding.bottom, // +34px on iPhone X right: context.isMobile ? 16 : 64, // 2. Chat TextField - Bottom padding EdgeInsets.only( bottom: 16 + max(insets.bottom, padding.bottom), ) // 3. Reviews Tab - List padding padding.addMore(bottom: context.padding.bottom), // 4. Call Screen - Bottom spacing SizedBox(height: 16 + context.padding.bottom), // 5. Become Provider Page - Top padding SizedBox(height: context.padding.top + 8), ``` ### 🎨 پیاده‌سازی در React: ```tsx // استفاده از CSS env() برای safe-area
``` **اما بهتر است از Flutter دریافت شود** چون: - Flutter دسترسی دقیق‌تر به system metrics دارد - `env()` در همه WebView‌ها پشتیبانی نمی‌شود - keyboard height در CSS قابل دسترسی نیست --- ## 🔄 ۳. تغییرات Real-time (باید لیسن شوند) ### ۱. **Keyboard Visibility** ```dart // زمانی که کیبورد باز/بسته می‌شود: MediaQuery.of(context).viewInsets.bottom ``` **Event به WebView:** ```json { "type": "KEYBOARD_CHANGED", "payload": { "visible": true, "height": 336, "animationDuration": 250 } } ``` **استفاده در React:** ```tsx useEffect(() => { window.addEventListener('message', (event) => { if (event.data.type === 'KEYBOARD_CHANGED') { const { height } = event.data.payload; // تنظیم bottom padding چت setChatBottomPadding(height + 16); } }); }, []); ``` --- ### ۲. **Screen Rotation / Size Change** ```dart // تغییر orientation یا resize MediaQuery.of(context).size ``` **Event به WebView:** ```json { "type": "SCREEN_SIZE_CHANGED", "payload": { "width": 812, "height": 375, "orientation": "landscape", "breakpoint": "mobile" } } ``` --- ### ۳. **Safe Area Changes** ```dart // تغییر وقتی که status bar مخفی/نمایش داده می‌شود MediaQuery.of(context).viewPadding ``` **Event به WebView:** ```json { "type": "SAFE_AREA_CHANGED", "payload": { "top": 44, "bottom": 34, "left": 0, "right": 0 } } ``` --- ## 🖥️ ۴. Design Sizes (Figma) ```dart // lib/core/util/design_size.dart static const Size mobileSize = Size(375, 814); static const Size tabletSize = Size(834, 1194); ``` ### ✅ باید به WebView ارسال شود: ```json { "designSize": { "mobile": { "width": 375, "height": 814 }, "tablet": { "width": 834, "height": 1194 } } } ``` **استفاده در React:** - برای محاسبه scaling factor - Responsive font sizes - Component sizing ```tsx const scaleFactor = window.innerWidth / (isMobile ? 375 : 834); const fontSize = 16 * scaleFactor; ``` --- ## 🎯 ۵. Platform & Environment Info ```dart // اطلاعات پلتفرم Platform.isIOS Platform.isAndroid ``` ### ✅ باید به WebView ارسال شود: ```json { "platform": { "os": "ios" | "android" | "web", "version": "17.2", "model": "iPhone 14 Pro", "isPhysicalDevice": true, "hasNotch": true, "hasDynamicIsland": true } } ``` ### 📱 تاثیر در UI: - iOS: Cupertino-style transitions - Android: Material ripples - Safe area handling مختلف - Font rendering --- ## 📦 ۶. JSON کامل Initial Config این JSON باید **یکبار هنگام load WebView** ارسال شود: ```json { "version": "1.0.0", "timestamp": 1234567890, "layout": { "breakpoint": "mobile", "screenWidth": 375, "screenHeight": 812, "isMobile": true, "isTablet": false, "isDesktop": false }, "safeArea": { "top": 44, "bottom": 34, "left": 0, "right": 0 }, "viewInsets": { "top": 0, "bottom": 0, "left": 0, "right": 0 }, "designSize": { "mobile": { "width": 375, "height": 814 }, "tablet": { "width": 834, "height": 1194 } }, "platform": { "os": "ios", "version": "17.2", "model": "iPhone 14 Pro", "isPhysicalDevice": true, "hasNotch": true, "hasDynamicIsland": true }, "locale": { "languageCode": "en", "countryCode": "US", "isRtl": false }, "theme": { "mode": "light", "primaryColor": "#FFB236" } } ``` --- ## 🔄 ۷. Events که باید به WebView ارسال شوند ### ۱. Layout Changed ```json { "type": "LAYOUT_CHANGED", "payload": { "breakpoint": "tablet", "screenWidth": 820, "isMobile": false } } ``` ### ۲. Keyboard Changed ```json { "type": "KEYBOARD_CHANGED", "payload": { "visible": true, "height": 336, "animationDuration": 250 } } ``` ### ۳. Safe Area Changed ```json { "type": "SAFE_AREA_CHANGED", "payload": { "top": 0, "bottom": 0, "left": 0, "right": 0 } } ``` ### ۴. Orientation Changed ```json { "type": "ORIENTATION_CHANGED", "payload": { "orientation": "landscape", "screenWidth": 812, "screenHeight": 375 } } ``` --- ## 💻 کد Flutter برای ارسال اطلاعات به WebView ```dart import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class TalkWebViewBridge { final WebViewController controller; BuildContext context; TalkWebViewBridge(this.controller, this.context) { _sendInitialConfig(); _listenToChanges(); } // ارسال اولیه config void _sendInitialConfig() { final config = jsonEncode({ 'version': '1.0.0', 'timestamp': DateTime.now().millisecondsSinceEpoch, 'layout': { 'breakpoint': _getBreakpoint(), 'screenWidth': MediaQuery.of(context).size.width, 'screenHeight': MediaQuery.of(context).size.height, 'isMobile': context.isMobile, 'isTablet': context.isTablet, 'isDesktop': context.isDesktop, }, 'safeArea': { 'top': MediaQuery.of(context).padding.top, 'bottom': MediaQuery.of(context).padding.bottom, 'left': MediaQuery.of(context).padding.left, 'right': MediaQuery.of(context).padding.right, }, 'viewInsets': { 'top': MediaQuery.of(context).viewInsets.top, 'bottom': MediaQuery.of(context).viewInsets.bottom, 'left': MediaQuery.of(context).viewInsets.left, 'right': MediaQuery.of(context).viewInsets.right, }, 'designSize': { 'mobile': {'width': 375, 'height': 814}, 'tablet': {'width': 834, 'height': 1194}, }, 'platform': { 'os': Platform.operatingSystem, 'version': Platform.operatingSystemVersion, }, 'locale': { 'languageCode': context.languageCode, 'isRtl': context.isRtl, }, }); controller.runJavaScript(''' window.dispatchEvent(new CustomEvent('flutterConfig', { detail: $config })); '''); } // لیسن به تغییرات keyboard void _listenToChanges() { // در هر frame چک می‌کنیم که آیا viewInsets تغییر کرده WidgetsBinding.instance.addPostFrameCallback((_) { final currentInsets = MediaQuery.of(context).viewInsets; if (_lastInsets?.bottom != currentInsets.bottom) { _sendKeyboardChanged(currentInsets.bottom); _lastInsets = currentInsets; } _listenToChanges(); // تکرار }); } EdgeInsets? _lastInsets; void _sendKeyboardChanged(double height) { final event = jsonEncode({ 'type': 'KEYBOARD_CHANGED', 'payload': { 'visible': height > 0, 'height': height, 'animationDuration': 250, }, }); controller.runJavaScript(''' window.dispatchEvent(new CustomEvent('flutterEvent', { detail: $event })); '''); } String _getBreakpoint() { if (context.isMobile) return 'mobile'; if (context.isTablet) return 'tablet'; return 'desktop'; } } ``` --- ## 💻 کد React برای دریافت اطلاعات ```tsx // hooks/useFlutterLayout.ts import { useEffect, useState } from 'react'; interface FlutterLayout { breakpoint: 'mobile' | 'tablet' | 'desktop'; screenWidth: number; screenHeight: number; isMobile: boolean; isTablet: boolean; safeArea: { top: number; bottom: number; left: number; right: number; }; viewInsets: { bottom: number; }; } export function useFlutterLayout() { const [layout, setLayout] = useState({ breakpoint: 'mobile', screenWidth: 375, screenHeight: 812, isMobile: true, isTablet: false, safeArea: { top: 0, bottom: 0, left: 0, right: 0 }, viewInsets: { bottom: 0 }, }); useEffect(() => { // دریافت config اولیه const handleConfig = (event: CustomEvent) => { const config = event.detail; setLayout({ breakpoint: config.layout.breakpoint, screenWidth: config.layout.screenWidth, screenHeight: config.layout.screenHeight, isMobile: config.layout.isMobile, isTablet: config.layout.isTablet, safeArea: config.safeArea, viewInsets: config.viewInsets, }); }; // دریافت events const handleEvent = (event: CustomEvent) => { const { type, payload } = event.detail; if (type === 'KEYBOARD_CHANGED') { setLayout(prev => ({ ...prev, viewInsets: { ...prev.viewInsets, bottom: payload.height, }, })); } if (type === 'SAFE_AREA_CHANGED') { setLayout(prev => ({ ...prev, safeArea: payload, })); } }; window.addEventListener('flutterConfig', handleConfig as EventListener); window.addEventListener('flutterEvent', handleEvent as EventListener); return () => { window.removeEventListener('flutterConfig', handleConfig as EventListener); window.removeEventListener('flutterEvent', handleEvent as EventListener); }; }, []); return layout; } ``` ### استفاده در Component: ```tsx function ChatPage() { const layout = useFlutterLayout(); return (
{/* محتوای چت */}
); } ``` --- ## 📋 Checklist پیاده‌سازی ### Flutter Side: - [ ] ایجاد `TalkWebViewBridge` class - [ ] ارسال initial config هنگام load WebView - [ ] لیسن به `MediaQuery.of(context).viewInsets` برای keyboard - [ ] لیسن به `MediaQuery.of(context).size` برای rotation - [ ] ارسال events به WebView از طریق `runJavaScript` ### React Side: - [ ] ایجاد `useFlutterLayout` hook - [ ] لیسن به `flutterConfig` event - [ ] لیسن به `flutterEvent` event - [ ] استفاده از layout info در components - [ ] تست در موبایل/تبلت - [ ] تست با keyboard باز/بسته - [ ] تست در landscape/portrait --- ## 🎯 خلاصه ### اطلاعاتی که **یکبار** ارسال می‌شود: 1. ✅ Breakpoint (mobile/tablet) 2. ✅ Screen size 3. ✅ Safe area 4. ✅ Design sizes 5. ✅ Platform info 6. ✅ Locale/direction ### اطلاعاتی که **real-time** ارسال می‌شود: 1. 🔄 Keyboard height (viewInsets.bottom) 2. 🔄 Safe area changes 3. 🔄 Screen size changes (rotation) 4. 🔄 Breakpoint changes ### تاثیر اصلی در UI: - **Horizontal padding**: mobile=16px, tablet=64px - **Bottom padding**: safeArea.bottom + viewInsets.bottom - **Chat TextField**: adjust for keyboard - **Floating buttons**: offset by safe area