You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中使用Speech To Text Provider实现多组件复用及回调处理

用Provider封装Speech To Text实现多Widget复用

1. 添加依赖

首先在pubspec.yaml里引入所需依赖包:

dependencies:
  flutter:
    sdk: flutter
  speech_to_text: ^6.4.0 # 建议使用最新稳定版本
  provider: ^6.0.5 # 建议使用最新稳定版本

2. 实现SpeechToText Provider类

新建speech_provider.dart文件,把语音识别的核心逻辑、状态管理和回调处理都封装在这里:

import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_to_text.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_recognition_error.dart';

class SpeechProvider extends ChangeNotifier {
  final SpeechToText _speechToText = SpeechToText();
  bool _isListening = false;
  String _lastWords = '';
  String _lastError = '';
  String _currentStatus = '';

  // 对外暴露的状态变量
  bool get isListening => _isListening;
  String get lastWords => _lastWords;
  String get lastError => _lastError;
  String get currentStatus => _currentStatus;

  // 初始化语音识别,检查权限
  Future<void> initSpeech() async {
    final hasPermission = await _speechToText.initialize(
      onStatus: _onStatusChanged,
      onError: _onErrorOccurred,
    );
    if (!hasPermission) {
      _lastError = '未获取语音识别权限';
      notifyListeners();
    }
  }

  // 开始语音识别
  void startListening() async {
    if (!_speechToText.isAvailable) {
      await initSpeech();
    }
    _isListening = true;
    _lastWords = '';
    _lastError = '';
    notifyListeners();

    await _speechToText.listen(
      onResult: _onSpeechResult,
      listenMode: ListenMode.confirmation,
      cancelOnError: true,
    );
  }

  // 停止语音识别
  void stopListening() {
    _isListening = false;
    _speechToText.stop();
    notifyListeners();
  }

  // 识别结果回调处理
  void _onSpeechResult(SpeechRecognitionResult result) {
    _lastWords = result.recognizedWords;
    notifyListeners();
  }

  // 状态变化回调处理
  void _onStatusChanged(String status) {
    _currentStatus = status;
    notifyListeners();
  }

  // 错误回调处理
  void _onErrorOccurred(SpeechRecognitionError error) {
    _lastError = '${error.errorMsg} - ${error.permanent ? "永久错误" : "临时错误"}';
    _isListening = false;
    notifyListeners();
  }
}

3. 在应用根节点注入Provider

修改main.dart,用ChangeNotifierProvider包裹整个应用,让所有子Widget都能访问到这个Provider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'speech_provider.dart';
import 'speech_demo_page.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '语音转文本演示',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const SpeechDemoPage(),
    );
  }
}

4. 多Widget中调用语音功能

新建speech_demo_page.dart,拆分不同功能的Widget,共享Provider中的状态:

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('语音转文本演示')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            const SpeechControlButton(),
            const SizedBox(height: 20),
            const SpeechResultCard(),
            const SizedBox(height: 20),
            const SpeechStatusPanel(),
          ],
        ),
      ),
    );
  }
}

// 控制开始/停止的按钮Widget
class SpeechControlButton extends StatelessWidget {
  const SpeechControlButton({super.key});

  @override
  Widget build(BuildContext context) {
    final speechProvider = Provider.of<SpeechProvider>(context);
    return ElevatedButton(
      onPressed: speechProvider.isListening
          ? speechProvider.stopListening
          : speechProvider.startListening,
      child: Text(speechProvider.isListening ? '停止识别' : '开始说话'),
    );
  }
}

// 显示识别结果的卡片Widget
class SpeechResultCard extends StatelessWidget {
  const SpeechResultCard({super.key});

  @override
  Widget build(BuildContext context) {
    final speechProvider = Provider.of<SpeechProvider>(context);
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text(
          speechProvider.lastWords.isEmpty
              ? '等待语音输入...'
              : '识别内容:${speechProvider.lastWords}',
          style: const TextStyle(fontSize: 16),
        ),
      ),
    );
  }
}

// 显示状态和错误信息的面板Widget
class SpeechStatusPanel extends StatelessWidget {
  const SpeechStatusPanel({super.key});

  @override
  Widget build(BuildContext context) {
    final speechProvider = Provider.of<SpeechProvider>(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('当前状态:${speechProvider.currentStatus}', style: const TextStyle(color: Colors.grey)),
        if (speechProvider.lastError.isNotEmpty)
          Text('错误提示:${speechProvider.lastError}', style: const TextStyle(color: Colors.red)),
      ],
    );
  }
}

核心说明

  • 整个应用仅初始化一次SpeechToText实例,放在Provider中统一管理,避免重复初始化导致的异常。
  • 所有状态变更后调用notifyListeners(),确保依赖状态的Widget自动刷新。
  • onStatus、onError等回调统一在Provider内部处理,不用在每个Widget中重复编写逻辑。
  • 多个Widget通过Provider.of获取共享状态,实现跨Widget的语音功能复用。

内容的提问来源于stack exchange,提问作者teacherman_wsls

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:10:49