Android原生(Kotlin)与Flutter混合开发 - 设备控制与状态同步解决方案

  1. Kotlin 原生实现 (Android)

1.1 AndroidManifest.xml

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.afloia.smartconnect">

    <application
        android:name=".MainApplication"
        android:label="Smart Connect"
        android:icon="@mipmap/ic_launcher">
        
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".DeviceControlActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize"
            android:exported="true"/>
    </application>
</manifest>

1.2 MainActivity.kt

kotlin 复制代码
package com.afloia.smartconnect

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<Button>(R.id.btn_open_flutter).setOnClickListener {
            val intent = DeviceControlActivity.createIntent(
                context = this,
                deviceId = "dehumidifier_001",
                deviceName = "My Dehumidifier"
            )
            startActivity(intent)
        }
    }
}

1.3 DeviceControlActivity.kt

kotlin 复制代码
package com.afloia.smartconnect

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.util.concurrent.atomic.AtomicBoolean

class DeviceControlActivity : FlutterActivity() {
    private val channel = "com.afloia.smartconnect/flutter_post"
    private lateinit var deviceId: String
    private lateinit var deviceName: String
    
    // 设备状态
    private val isPowerOn = AtomicBoolean(false)
    private val uvSwitch = AtomicBoolean(true)
    private val ionSwitch = AtomicBoolean(true)
    private val dryingSwitch = AtomicBoolean(true)
    private var temperature = 22.3f
    private var humidity = 56f
    private var isRunning = AtomicBoolean(true)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        deviceId = intent.getStringExtra("deviceId") ?: "unknown"
        deviceName = intent.getStringExtra("deviceName") ?: "Device"
        
        Log.d("DeviceControl", "Starting for device: $deviceId")
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        
        // 启动传感器数据更新
        startSensorUpdates()

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channel).setMethodCallHandler { call, result ->
            when (call.method) {
                "getInitialState" -> handleGetInitialState(result)
                "togglePower" -> handleTogglePower(call, result)
                "toggleUV" -> handleToggleUV(call, result)
                "toggleIon" -> handleToggleIon(call, result)
                "toggleDrying" -> handleToggleDrying(call, result)
                "getSensorData" -> handleGetSensorData(result)
                "pop" -> handlePop(result)
                else -> result.notImplemented()
            }
        }
    }

    private fun handleGetInitialState(result: MethodChannel.Result) {
        val state = JSONObject().apply {
            put("isPowerOn", isPowerOn.get())
            put("uvSwitch", uvSwitch.get())
            put("ionSwitch", ionSwitch.get())
            put("dryingSwitch", dryingSwitch.get())
            put("temperature", temperature)
            put("humidity", humidity)
            put("deviceName", deviceName)
        }
        result.success(state.toString())
    }

    private fun handleTogglePower(call: MethodChannel.MethodCall, result: MethodChannel.Result) {
        val isOn = call.argument<Boolean>("isOn") ?: false
        Log.d("DeviceControl", "Toggle power to: $isOn")
        
        // 模拟设备控制延迟
        lifecycleScope.launch {
            withContext(Dispatchers.IO) {
                delay(300) // 模拟网络/硬件延迟
                isPowerOn.set(isOn)
                result.success(isOn)
            }
        }
    }

    private fun handleToggleUV(call: MethodChannel.MethodCall, result: MethodChannel.Result) {
        val isOn = call.argument<Boolean>("isOn") ?: false
        uvSwitch.set(isOn)
        result.success(isOn)
    }

    private fun handleToggleIon(call: MethodChannel.MethodCall, result: MethodChannel.Result) {
        val isOn = call.argument<Boolean>("isOn") ?: false
        ionSwitch.set(isOn)
        result.success(isOn)
    }

    private fun handleToggleDrying(call: MethodChannel.MethodCall, result: MethodChannel.Result) {
        val isOn = call.argument<Boolean>("isOn") ?: false
        dryingSwitch.set(isOn)
        result.success(isOn)
    }

    private fun handleGetSensorData(result: MethodChannel.Result) {
        val data = JSONObject().apply {
            put("temperature", temperature)
            put("humidity", humidity)
        }
        result.success(data.toString())
    }

    private fun handlePop(result: MethodChannel.Result) {
        finish()
        result.success(null)
    }

    private fun startSensorUpdates() {
        lifecycleScope.launch {
            while (isRunning.get()) {
                delay(5000) // 每5秒更新一次
                
                // 模拟传感器数据变化
                temperature += 0.1f
                humidity -= 0.5f
                
                // 保持数据在合理范围内
                if (temperature > 30f) temperature = 22f
                if (humidity < 30f) humidity = 60f
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        isRunning.set(false)
        Log.d("DeviceControl", "Activity destroyed")
    }

    companion object {
        fun createIntent(
            context: Context,
            deviceId: String,
            deviceName: String
        ): Intent {
            return FlutterActivity.withNewEngine()
                .initialRoute("/deviceControl")
                .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.opaque)
                .build(context)
                .apply {
                    putExtra("deviceId", deviceId)
                    putExtra("deviceName", deviceName)
                    setClass(context, DeviceControlActivity::class.java)
                }
        }
    }
}

1.4 activity_main.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="16dp">

    <Button
        android:id="@+id/btn_open_flutter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Open Flutter Device Control"
        android:textAllCaps="false"
        android:padding="16dp"/>

</LinearLayout>
  1. Flutter 完整实现

2.1 main.dart

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dehumidifier App',
      theme: ThemeData(
        brightness: Brightness.light,
        fontFamily: 'Roboto',
        colorScheme: ColorScheme.fromSwatch(
          primarySwatch: Colors.blue,
          accentColor: Colors.blueAccent,
        ),
      ),
      home: const HomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Device List'),
      ),
      body: Center(
        child: ElevatedButton(
          style: ElevatedButton.styleFrom(
            padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
            textStyle: const TextStyle(fontSize: 18),
          ),
          child: const Text('Open Dehumidifier Control'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const DehumidifierPage(),
              ),
            );
          },
        ),
      ),
    );
  }
}

2.2 dehumidifier_page.dart

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class DehumidifierPage extends StatefulWidget {
  const DehumidifierPage({super.key});

  @override
  State<DehumidifierPage> createState() => _DehumidifierPageState();
}

class _DehumidifierPageState extends State<DehumidifierPage> {
  static const platform = MethodChannel('com.afloia.smartconnect/flutter_post');
  
  bool _isPowerOn = false;
  bool _uvSwitch = true;
  bool _ionSwitch = true;
  bool _dryingSwitch = true;
  double _temperature = 22.3;
  double _humidity = 56.0;
  bool _isLoading = true;
  String _deviceName = "My Dehumidifier";
  bool _isWaitingForResponse = false;

  @override
  void initState() {
    super.initState();
    _initializeDevice();
    _startSensorUpdates();
  }

  Future<void> _initializeDevice() async {
    try {
      setState(() => _isLoading = true);
      final initialState = await platform.invokeMethod('getInitialState');
      
      if (initialState is String) {
        final data = _parseJson(initialState);
        setState(() {
          _isPowerOn = data['isPowerOn'] ?? false;
          _uvSwitch = data['uvSwitch'] ?? true;
          _ionSwitch = data['ionSwitch'] ?? true;
          _dryingSwitch = data['dryingSwitch'] ?? true;
          _temperature = (data['temperature'] as num?)?.toDouble() ?? 22.3;
          _humidity = (data['humidity'] as num?)?.toDouble() ?? 56.0;
          _deviceName = data['deviceName']?.toString() ?? "My Dehumidifier";
          _isLoading = false;
        });
      }
    } on PlatformException catch (e) {
      _showError('Initialization failed: ${e.message}');
      setState(() => _isLoading = false);
    } catch (e) {
      _showError('Unexpected error: $e');
      setState(() => _isLoading = false);
    }
  }

  Map<String, dynamic> _parseJson(String jsonString) {
    try {
      return jsonString
          .replaceFirst("{", "")
          .replaceFirst("}", "")
          .split(",")
          .map((e) => e.split(":"))
          .fold({}, (map, element) {
            if (element.length == 2) {
              final key = element[0].trim().replaceAll("\"", "");
              var value = element[1].trim();
              
              if (value == "true") value = "1";
              if (value == "false") value = "0";
              
              map[key] = num.tryParse(value) ?? value.replaceAll("\"", "");
            }
            return map;
          });
    } catch (e) {
      return {};
    }
  }

  void _startSensorUpdates() {
    const sensorUpdateInterval = Duration(seconds: 5);
    
    Future.doWhile(() async {
      await Future.delayed(sensorUpdateInterval);
      if (!mounted) return false;
      
      try {
        final sensorData = await platform.invokeMethod('getSensorData');
        if (sensorData is String) {
          final data = _parseJson(sensorData);
          setState(() {
            _temperature = (data['temperature'] as num?)?.toDouble() ?? _temperature;
            _humidity = (data['humidity'] as num?)?.toDouble() ?? _humidity;
          });
        }
      } on PlatformException catch (e) {
        debugPrint('Failed to get sensor data: ${e.message}');
      }
      return true;
    });
  }

  Future<void> _togglePower() async {
    if (_isWaitingForResponse) return;
    
    setState(() => _isWaitingForResponse = true);
    try {
      final newState = !_isPowerOn;
      final result = await platform.invokeMethod('togglePower', {'isOn': newState});
      
      if (result == true) {
        setState(() => _isPowerOn = newState);
      } else {
        _showError('Failed to toggle power');
      }
    } on PlatformException catch (e) {
      _showError('Communication error: ${e.message}');
    } finally {
      if (mounted) {
        setState(() => _isWaitingForResponse = false);
      }
    }
  }

  Future<void> _toggleUV(bool value) async {
    try {
      final result = await platform.invokeMethod('toggleUV', {'isOn': value});
      if (result == true) {
        setState(() => _uvSwitch = value);
      }
    } on PlatformException catch (e) {
      _showError('UV control failed: ${e.message}');
    }
  }

  Future<void> _toggleIon(bool value) async {
    try {
      final result = await platform.invokeMethod('toggleIon', {'isOn': value});
      if (result == true) {
        setState(() => _ionSwitch = value);
      }
    } on PlatformException catch (e) {
      _showError('Ion control failed: ${e.message}');
    }
  }

  Future<void> _toggleDrying(bool value) async {
    try {
      final result = await platform.invokeMethod('toggleDrying', {'isOn': value});
      if (result == true) {
        setState(() => _dryingSwitch = value);
      }
    } on PlatformException catch (e) {
      _showError('Drying control failed: ${e.message}');
    }
  }

  void _showError(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        duration: const Duration(seconds: 2),
      ),
    );
  }

  Future<bool> _onWillPop() async {
    try {
      await platform.invokeMethod('pop');
      return true;
    } on PlatformException catch (e) {
      debugPrint('Failed to pop: ${e.message}');
      return true;
    }
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: _onWillPop,
      child: Scaffold(
        body: _isLoading
            ? const Center(child: CircularProgressIndicator())
            : Container(
                decoration: const BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      Color(0xFF8A98E8),
                      Color(0xFF9FABF2),
                    ],
                  ),
                ),
                child: Column(
                  children: [
                    _buildAppBar(context),
                    Expanded(
                      child: ListView(
                        padding: const EdgeInsets.symmetric(horizontal: 16.0),
                        children: [
                          _buildStatusSection(),
                          const SizedBox(height: 20),
                          _buildPowerControl(),
                          const SizedBox(height: 12),
                          _buildModeAndSpeedControls(),
                          const SizedBox(height: 12),
                          _buildTimerControl(),
                          const SizedBox(height: 12),
                          _buildToggleControl(
                            icon: Icons.flare,
                            title: 'UV Sterilization',
                            value: _uvSwitch,
                            onChanged: _toggleUV,
                          ),
                          const SizedBox(height: 12),
                          _buildToggleControl(
                            icon: Icons.air,
                            title: 'Negative Ion',
                            value: _ionSwitch,
                            onChanged: _toggleIon,
                          ),
                          const SizedBox(height: 12),
                          _buildToggleControl(
                            icon: Icons.dry,
                            title: 'Auto Drying',
                            value: _dryingSwitch,
                            onChanged: _toggleDrying,
                          ),
                          const SizedBox(height: 20),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
      ),
    );
  }

  Widget _buildAppBar(BuildContext context) {
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            IconButton(
              icon: const Icon(Icons.arrow_back_ios, color: Colors.white),
              onPressed: () => _onWillPop().then((value) {
                if (value) Navigator.maybePop(context);
              }),
            ),
            Text(
              _deviceName,
              style: const TextStyle(
                color: Colors.white,
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            IconButton(
              icon: const Icon(Icons.list, color: Colors.white, size: 30),
              onPressed: () {
                // TODO: Implement menu action
              },
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusSection() {
    return Column(
      children: [
        const SizedBox(height: 20),
        const Text(
          'Comfort',
          style: TextStyle(
            color: Colors.white,
            fontSize: 48,
            fontWeight: FontWeight.w300,
          ),
        ),
        const SizedBox(height: 30),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Column(
              children: [
                Text(
                  _temperature.toStringAsFixed(1),
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 40,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const Text(
                  'Temperature (°C)',
                  style: TextStyle(color: Colors.white70, fontSize: 14),
                ),
              ],
            ),
            const SizedBox(
              height: 50,
              child: VerticalDivider(
                color: Colors.white54,
                thickness: 1,
              ),
            ),
            Column(
              children: [
                Text(
                  _humidity.toStringAsFixed(0),
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 40,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const Text(
                  'Humidity (%)',
                  style: TextStyle(color: Colors.white70, fontSize: 14),
                ),
              ],
            ),
          ],
        ),
        const SizedBox(height: 30),
      ],
    );
  }

  Widget _buildPowerControl() {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ListTile(
        onTap: _isWaitingForResponse ? null : _togglePower,
        leading: Icon(
          Icons.power_settings_new,
          color: _isPowerOn ? Colors.green : Colors.blue,
          size: 30,
        ),
        title: const Text('Power', 
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
        trailing: Text(
          _isPowerOn ? 'ON' : 'OFF',
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.bold,
            color: _isPowerOn ? Colors.green : Colors.grey,
          ),
        ),
      ),
    );
  }

  Widget _buildModeAndSpeedControls() {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            _buildIconTextButton(icon: Icons.layers, label: 'Mode'),
            const SizedBox(height: 50, child: VerticalDivider(thickness: 1)),
            _buildIconTextButton(icon: Icons.nightlight_round, label: 'Speed'),
          ],
        ),
      ),
    );
  }

  Widget _buildIconTextButton({required IconData icon, required String label}) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Icon(icon, color: Colors.blue, size: 30),
        const SizedBox(height: 4),
        Text(label, style: const TextStyle(fontSize: 14, color: Colors.black54)),
      ],
    );
  }

  Widget _buildTimerControl() {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: const ListTile(
        leading: Icon(Icons.timer_outlined, color: Colors.grey, size: 30),
        title: Text('Timer', 
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
        trailing: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text('Off', style: TextStyle(color: Colors.grey, fontSize: 16)),
            Icon(Icons.arrow_forward_ios, color: Colors.grey, size: 16),
          ],
        ),
      ),
    );
  }

  Widget _buildToggleControl({
    required IconData icon,
    required String title,
    required bool value,
    required ValueChanged<bool> onChanged,
  }) {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ListTile(
        leading: Icon(icon, color: Colors.grey, size: 30),
        title: Text(title, 
            style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
        trailing: Switch(
          value: value,
          onChanged: onChanged,
          activeColor: Colors.blue,
        ),
      ),
    );
  }
}

关键功能说明

  1. 原生与Flutter通信:
    · 使用MethodChannel进行双向通信
    · 处理所有设备控制命令和状态更新
  2. 状态管理:
    · 完整的状态同步机制
    · 定期传感器数据更新
    · 加载状态和错误处理
  3. UI交互:
    · 响应式UI设计
    · 禁用按钮防止重复操作
    · 友好的错误提示
  4. 生命周期管理:
    · 正确处理Activity和Flutter引擎生命周期
    · 取消后台任务防止内存泄漏
  5. 类型安全:
    · Kotlin和Dart都使用了严格的类型检查
    · 处理所有可能的异常情况

这个实现提供了完整的端到端解决方案,可以直接集成到您的项目中。所有关键部分都已实现

相关推荐
ujainu7 小时前
Flutter + OpenHarmony 游戏开发进阶:用户输入响应——GestureDetector 实现点击发射
flutter·游戏·openharmony
Doro再努力7 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
Daniel李华7 小时前
echarts使用案例
android·javascript·echarts
hudawei9967 小时前
TweenAnimationBuilder和AnimatedBuilder两种动画的比较
flutter·ui·动画·tweenanimation·animatedbuilder
ujainu7 小时前
Flutter + OpenHarmony 实现无限跑酷游戏开发实战—— 对象池化、性能优化与流畅控制
flutter·游戏·性能优化·openharmony·endless runner
做人不要太理性8 小时前
CANN Runtime 运行时组件深度解析:任务调度机制、存储管理策略与维测体系构建逻辑
android·运维·魔珐星云
我命由我123458 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime
ZH15455891318 小时前
Flutter for OpenHarmony Python学习助手实战:自动化脚本开发的实现
python·学习·flutter
朗迹 - 张伟9 小时前
Tauri2 导出 Android 详细教程
android
lpruoyu9 小时前
【Android第一行代码学习笔记】Android架构_四大组件_权限_持久化_通知_异步_服务
android·笔记·学习