# 📐 اطلاعات 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