从 Kotlin ViewModel 到 Flutter:完整迁移指南与状态管理实践

发布于:2025-08-12 ⋅ 阅读:(16) ⋅ 点赞:(0)

将 Kotlin + ViewModel 迁移到 Flutter

要将现有的 Kotlin + ViewModel 架构迁移到 Flutter,你需要理解 Flutter 的状态管理方式与 Android ViewModel 的区别。以下是一个完整的迁移指南:

1. 架构对比

Android (Kotlin + ViewModel)

  • ViewModel: 管理 UI 相关数据,生命周期感知
  • LiveData/StateFlow: 观察数据变化
  • Data Binding/View Binding: 绑定 UI

Flutter 等效方案

  • 状态管理: Provider, Riverpod, Bloc, GetX 等 (替代 ViewModel)
  • 状态观察: ValueNotifier, Stream, StatefulWidget
  • UI 绑定: Widget 直接使用状态

2. 迁移步骤

2.1 创建等效的状态管理

假设你有一个简单的计数器 ViewModel:

// Android 原代码
class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count
    
    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

Flutter 等效实现 (使用 Provider):

// Flutter 替代方案
class CounterProvider with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  
  void increment() {
    _count++;
    notifyListeners();
  }
}

2.2 迁移 UI 层

Android XML/Kotlin:

// 原 Android 代码
class CounterActivity : AppCompatActivity() {
    private lateinit var viewModel: CounterViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_counter)
        
        viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
        
        val counterText = findViewById<TextView>(R.id.counter_text)
        val incrementBtn = findViewById<Button>(R.id.increment_btn)
        
        viewModel.count.observe(this) { count ->
            counterText.text = count.toString()
        }
        
        incrementBtn.setOnClickListener {
            viewModel.increment()
        }
    }
}

Flutter 等效 Widget:

// Flutter 替代代码
class CounterPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final counterProvider = Provider.of<CounterProvider>(context);
    
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: ${counterProvider.count}'),
            ElevatedButton(
              onPressed: () => counterProvider.increment(),
              child: Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

2.3 设置 Provider

在应用顶层设置 Provider:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterProvider(),
      child: MyApp(),
    ),
  );
}

3. 高级场景迁移

3.1 处理异步操作

原 Kotlin ViewModel 可能包含协程:

class UserViewModel : ViewModel() {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user
    
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _user.value = repository.getUser(userId)
        }
    }
}

Flutter 等效:

class UserProvider with ChangeNotifier {
  User? _user;
  User? get user => _user;
  
  Future<void> loadUser(String userId) async {
    _user = await repository.getUser(userId);
    notifyListeners();
  }
}

3.2 导航迁移

Android 导航:

findNavController().navigate(R.id.action_to_details)

Flutter 导航:

Navigator.of(context).push(MaterialPageRoute(
  builder: (context) => DetailsPage(),
));

4. 推荐的状态管理方案

  1. Provider + ChangeNotifier: 最接近 ViewModel 的概念
  2. Riverpod: Provider 的改进版,更灵活
  3. Bloc: 适合复杂业务逻辑
  4. GetX: 简单易用,集成路由管理

5. 完整示例

// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterProvider(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Counter',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: CounterPage(),
    );
  }
}

class CounterProvider with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  
  void increment() {
    _count++;
    notifyListeners();
  }
}

class CounterPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final counter = Provider.of<CounterProvider>(context);
    
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('You have pushed the button this many times:'),
            Text('${counter.count}', style: Theme.of(context).textTheme.headline4),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

6. 迁移建议

  1. 逐步迁移: 可以一次迁移一个页面,使用 Flutter 的 PlatformViewMethodChannel 在混合应用中逐步替换
  2. 业务逻辑复用: 将核心业务逻辑提取为纯 Dart 类,可在两端共享
  3. 测试验证: 确保迁移后的行为与原生应用一致

通过这种方式,你可以将 Android 的 ViewModel 模式平滑迁移到 Flutter 的状态管理方案中。