Flutter for OpenHarmony 实战:Supabase — 跨平台后端服务首选

前言

在构筑 Flutter for OpenHarmony 应用时,开发者往往面临后端选型的难题。是自建服务器处理复杂的权限与存储,还是寻找一个现成的云服务?Supabase 作为开源版的 Firebase,凭借其基于 PostgreSQL 的强大能力、实时数据推送以及自带的 Auth 认证系统,成为了鸿蒙跨平台应用的首选后端方案。

在鸿蒙系统上,Supabase 通过纯 Dart 的客户端库能完美运行,无需担心平台底层的兼容性问题。本文将带你从零开始,在鸿蒙设备上打通 Supabase 的全流程。


一、为什么集成 Supabase?

1.1 开源与透明 🔗

Supabase 完全开源,这意味着你可以随时将其私有化部署在自己的鸿蒙服务器或私有云中,数据掌控度极高。

1.2 PostgreSQL 的硬实力

不同于 NoSQL,Supabase 底层是标准的 PostgreSQL。这让鸿蒙开发者可以利用外键约束、视图(Views)和触发器(Triggers)来处理复杂的业务逻辑,而不仅仅是简单的键值对。


二、配置环境 📦

在项目中引入 Supabase 核心包。

dependencies:
  supabase_flutter: ^2.6.0

main.dart 中进行初始化:

import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> main() async {
  await Supabase.initialize(
    url: 'https://your-project.supabase.co',
    anonKey: 'your-public-anon-key',
  );
  runApp(const MyApp());
}

💡 注意:在正式发布鸿蒙应用前,请确保在 Supabase 后端配置正确的 Webhook 地址及 CORS 策略。


三、核心功能:3 个场景化进阶用法

3.1 极简身份认证 (Authentication)

利用 Supabase 自带的认证系统,一行代码实现鸿蒙应用登录。

Future<void> signIn(String email, String password) async {
  final response = await Supabase.instance.client.auth.signInWithPassword(
    email: email,
    password: password,
  );
  if (response.user != null) {
    print('✅ 鸿蒙用户登录成功:${response.user!.id}');
  }
}

在这里插入图片描述

3.2 响应式实时订阅 (Real-time)

当云端数据发生变动时,鸿蒙侧 UI 会通过 Stream 自动刷新,非常适合即时通讯场景。

final _stream = Supabase.instance.client
    .from('messages')
    .stream(primaryKey: ['id'])
    .order('created_at');

// 在 Widget 中配合 StreamBuilder 使用

在这里插入图片描述

3.3 远程文件存储 (Storage)

将用户在鸿蒙设备上拍摄的照片直接上传至 Supabase 存储桶。

Future<void> uploadAvatar(File imageFile) async {
  final path = 'public/avatars/${DateTime.now().toIso8601String()}.png';
  await Supabase.instance.client.storage
      .from('user_assets')
      .upload(path, imageFile);
  print('✅ 资源已同步至云端');
}

在这里插入图片描述


四、OpenHarmony 平台适配建议

4.1 网络访问权限申请 🏗️

⚠️ 注意:鸿蒙应用访问外部 Supabase 域名需要显式声明权限。

  • ✅ 建议做法:在 module.json5requestPermissions 列表中加入 ohos.permission.INTERNET

4.2 处理 OAuth 回调

  • 💡 技巧:在使用 Google 或 GitHub 第三方登录时,鸿蒙端的 deep link 协议配置至关重要。确保在后台设置的 Redirect URL 能正确拉起你的鸿蒙应用 Scheme。

五、完整实战示例:鸿蒙云端协作待办应用

我们将构建一个全栈实战案例:一个支持云端同步与认证的 Todo 应用。无论在哪个鸿蒙设备上登录相同的账号,数据都会保持同步。

import 'dart:async';
import 'package:flutter/material.dart';

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

  
  State<OhosTodoAppPage> createState() => _OhosTodoAppPageState();
}

class _OhosTodoAppPageState extends State<OhosTodoAppPage> {
  final List<Map<String, dynamic>> _todos = [
    {'id': 1, 'content': '完成鸿蒙单元测试', 'is_complete': true},
    {'id': 2, 'content': '集成 Supabase Auth', 'is_complete': false},
  ];

  final TextEditingController _controller = TextEditingController();
  final StreamController<List<Map<String, dynamic>>> _todoStream =
      StreamController<List<Map<String, dynamic>>>();

  
  void initState() {
    super.initState();
    _todoStream.add(_todos);
  }

  void _addTodo() {
    if (_controller.text.isEmpty) {
      return;
    }
    setState(() {
      _todos.insert(0, {
        'id': DateTime.now().millisecondsSinceEpoch,
        'content': _controller.text,
        'is_complete': false
      });
      _todoStream.add(List.from(_todos));
      _controller.clear();
    });
    // 💡 实战提示:真实环境下应调用 client.from('todos').insert(...)
    ScaffoldMessenger.of(context)
        .showSnackBar(const SnackBar(content: Text('✅ 已同步至 Supabase Cloud')));
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF8F9FB),
      appBar: AppBar(
        title: const Text('鸿蒙 × Supabase 协作待办'),
        elevation: 0,
        backgroundColor: Colors.indigo,
        foregroundColor: Colors.white,
      ),
      body: Column(
        children: [
          _buildInputArea(),
          Expanded(
            child: StreamBuilder<List<Map<String, dynamic>>>(
              stream: _todoStream.stream,
              builder: (context, snapshot) {
                if (!snapshot.hasData) {
                  return const Center(child: CircularProgressIndicator());
                }
                final items = snapshot.data!;
                return ListView.builder(
                  padding: const EdgeInsets.all(16),
                  itemCount: items.length,
                  itemBuilder: (context, index) {
                    final item = items[index];
                    return _buildTodoItem(item);
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildInputArea() {
    return Container(
      padding: const EdgeInsets.all(20),
      decoration: const BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)),
      ),
      child: TextField(
        controller: _controller,
        decoration: InputDecoration(
          hintText: '输入新任务...',
          prefixIcon: const Icon(Icons.edit_note, color: Colors.indigo),
          suffixIcon: IconButton(
            icon: const Icon(Icons.send_rounded, color: Colors.indigo),
            onPressed: _addTodo,
          ),
          filled: true,
          fillColor: Colors.grey[100],
          border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(16),
              borderSide: BorderSide.none),
        ),
        onSubmitted: (_) => _addTodo(),
      ),
    );
  }

  Widget _buildTodoItem(Map<String, dynamic> item) {
    return Container(
      margin: const EdgeInsets.only(bottom: 12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(color: Colors.black.withOpacity(0.02), blurRadius: 10)
        ],
      ),
      child: CheckboxListTile(
        title: Text(
          item['content'],
          style: TextStyle(
            decoration: item['is_complete'] ? TextDecoration.lineThrough : null,
            color: item['is_complete'] ? Colors.grey : Colors.black87,
          ),
        ),
        value: item['is_complete'],
        onChanged: (val) {
          setState(() {
            item['is_complete'] = val;
            _todoStream.add(List.from(_todos));
          });
        },
        activeColor: Colors.indigo,
        checkboxShape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
      ),
    );
  }
}

在这里插入图片描述


六、总结

Flutter for OpenHarmony 开发中,Supabase 提供了一套免运维、高性能的后端底座。它将原本繁琐的服务器开发工作简化成了简单的 SDK 调用,让前端开发者也能轻松构建具备认证与实时特性的复杂全栈应用。

如果你正在构思一个需要云端大脑的鸿蒙 App,Supabase 绝对值得作为你的第一选择。


🌐 欢迎加入开源鸿蒙跨平台社区开源鸿蒙跨平台开发者社区

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐