【Flutter】Flutter_tts로 텍스트 낭독 프로그램 만들기 [전문 유·복사 붙여넣기 OK]

74071 단어 FlutterDartttstech
네, 그것도 좋아요.라고 보도한 지 16일째다.

안부를 묻다


안녕하세요, 저는 흰 사과입니다.
이전 Fluttertts를 해 본 샘플이 있어서 소개해 드릴게요.
전문 코드가 실렸으니, 마음에 드는 사람은 복사해 보세요.

차리다


"fluter tts"패키지와 "fluter localization"을 미리 설치합니다.
코무 IT 학원 어드벤트 캘린더 2021
pubspec.yaml
dependencies:
  flutter_tts:
  flutter_localizations:
    sdk: flutter
MinsdkVersion은 안드로이드에서 21로 지정됩니다.
android/app/build.gradle
defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.example.camera_app"
    minSdkVersion 21
    targetSdkVersion 29
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}
이것으로 끝낼 준비를 하다.

코드 전체 텍스트


main.dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_tts/flutter_tts.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  
  _MyAppState createState() => _MyAppState();
}

enum TtsState { playing, stopped, paused, continued }

class _MyAppState extends State<MyApp> {
  FlutterTts flutterTts;
  String language;
  String engine;
  double volume = 0.5;
  double pitch = 1.0;
  double rate = 0.5;
  bool isCurrentLanguageInstalled = false;

  String _newVoiceText;
  var _controller = TextEditingController();

  TtsState ttsState = TtsState.stopped;

  get isPlaying => ttsState == TtsState.playing;

  get isStopped => ttsState == TtsState.stopped;

  get isPaused => ttsState == TtsState.paused;

  get isContinued => ttsState == TtsState.continued;

  
  initState() {
    super.initState();
    initTts();
  }

  initTts() {
    flutterTts = FlutterTts();

    flutterTts.setStartHandler(() {
      setState(() {
        ttsState = TtsState.playing;
      });
    });

    flutterTts.setCompletionHandler(() {
      setState(() {
        ttsState = TtsState.stopped;
      });
    });

    flutterTts.setCancelHandler(() {
      setState(() {
        ttsState = TtsState.stopped;
      });
    });

    flutterTts.setErrorHandler((msg) {
      setState(() {
        ttsState = TtsState.stopped;
      });
    });
  }

  Future<dynamic> _getLanguages() => flutterTts.getLanguages;

  Future _speak() async {
    await flutterTts.setVolume(volume);
    await flutterTts.setSpeechRate(rate);
    await flutterTts.setPitch(pitch);

    if (_newVoiceText != null) {
      if (_newVoiceText.isNotEmpty) {
        await flutterTts.awaitSpeakCompletion(true);
        await flutterTts.speak(_newVoiceText);
      }
    }
  }

  Future _stop() async {
    var result = await flutterTts.stop();
    if (result == 1) setState(() => ttsState = TtsState.stopped);
  }

  Future _pause() async {
    var result = await flutterTts.pause();
    if (result == 1) setState(() => ttsState = TtsState.paused);
  }

  
  void dispose() {
    super.dispose();
    flutterTts.stop();
  }

  List<DropdownMenuItem<String>> getEnginesDropDownMenuItems(dynamic engines) {
    var items = <DropdownMenuItem<String>>[];
    for (dynamic type in engines) {
      items.add(
          DropdownMenuItem(value: type as String, child: Text(type as String)));
    }
    return items;
  }

  void changedEnginesDropDownItem(String selectedEngine) {
    flutterTts.setEngine(selectedEngine);
    language = null;
    setState(() {
      engine = selectedEngine;
    });
  }

  List<DropdownMenuItem<String>> getLanguageDropDownMenuItems(
      dynamic languages) {
    var items = <DropdownMenuItem<String>>[];
    for (dynamic type in languages) {
      items.add(
        DropdownMenuItem(
          value: type as String,
          child: Text(type as String),
        ),
      );
    }
    return items;
  }

  void changedLanguageDropDownItem(String selectedType) {
    setState(() {
      language = selectedType;
      flutterTts.setLanguage(language);
    });
  }

  void _onChange(String text) {
    setState(() {
      _newVoiceText = text;
    });
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      supportedLocales: [Locale('ja', 'JP')],
      locale: Locale('ja', 'JP'),
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'TextToSpeech',
            style: TextStyle(color: Colors.black),
          ),
          backgroundColor: Colors.white,
        ),
        body: GestureDetector(
          onTap: () {
            FocusScope.of(context).requestFocus(new FocusNode());
          },
          child: SingleChildScrollView(
            scrollDirection: Axis.vertical,
            child: Column(
              children: [
                _inputSection(),
                _btnSection(),
                _futureBuilder(),
                _buildSliders(),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _futureBuilder() => FutureBuilder<dynamic>(
      future: _getLanguages(),
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        if (snapshot.hasData) {
          return _languageDropDownSection(snapshot.data);
        } else if (snapshot.hasError) {
          return Text('言語の読み込みに失敗');
        } else
          return Text('読み込み中');
      });

  Widget _inputSection() => Container(
      alignment: Alignment.topCenter,
      padding: EdgeInsets.only(top: 25.0, left: 25.0, right: 25.0),
      child: TextField(
        controller: _controller,
        enabled: true,
        keyboardType: TextInputType.multiline,
        maxLines: null,
        minLines: 3,
        decoration: InputDecoration(
          suffixIcon: IconButton(
            onPressed: () => _controller.clear(),
            icon: Icon(
              Icons.clear,
              color: Colors.grey,
            ),
          ),
        ),
        onChanged: (String value) {
          _onChange(value);
        },
      ));

  Widget _btnSection() {
    return Container(
      padding: EdgeInsets.only(top: 50.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          _buildButtonColumn(
            Colors.green,
            Colors.greenAccent,
            Icons.play_arrow,
            '再生',
            _speak,
          ),
          _buildButtonColumn(
            Colors.red,
            Colors.redAccent,
            Icons.stop,
            'リセット',
            _stop,
          ),
          _buildButtonColumn(
            Colors.blue,
            Colors.blueAccent,
            Icons.pause,
            '停止',
            _pause,
          ),
        ],
      ),
    );
  }

  Widget _languageDropDownSection(dynamic languages) {
    return Container(
      padding: EdgeInsets.only(top: 10.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          DropdownButton(
            value: language,
            items: getLanguageDropDownMenuItems(languages),
            onChanged: changedLanguageDropDownItem,
          ),
        ],
      ),
    );
  }

  Column _buildButtonColumn(
      Color color,
      Color splashColor,
      IconData icon,
      String label,
      Function func,
      ) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        IconButton(
          icon: Icon(icon),
          color: color,
          splashColor: splashColor,
          onPressed: () => func(),
        ),
        Container(
          margin: const EdgeInsets.only(top: 8.0),
          child: Text(
            label,
            style: TextStyle(
              fontSize: 12.0,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildSliders() {
    return Column(
      children: [_volume(), _pitch(), _rate()],
    );
  }

  Widget _volume() {
    return Slider(
        value: volume,
        onChanged: (newVolume) {
          setState(() => volume = newVolume);
        },
        min: 0.0,
        max: 1.0,
        divisions: 10,
        label: "音量: $volume");
  }

  Widget _pitch() {
    return Slider(
      value: pitch,
      onChanged: (newPitch) {
        setState(() => pitch = newPitch);
      },
      min: 0.5,
      max: 2.0,
      divisions: 15,
      label: "声の高さ: $pitch",
      activeColor: Colors.red,
    );
  }

  Widget _rate() {
    return Slider(
      value: rate,
      onChanged: (newRate) {
        setState(() => rate = newRate);
      },
      min: 0.0,
      max: 1.0,
      divisions: 10,
      label: "スピード: $rate",
      activeColor: Colors.green,
    );
  }
}


해설


거의'fluter tts'의 샘플과 마찬가지로 iOS 실기 테스트에 사용할 수 있도록 곳곳에서 수정되었다.

사진에서
텍스트 쓰기 "inputSection"
main.dart
Widget _inputSection() => Container(
      alignment: Alignment.topCenter,
      padding: EdgeInsets.only(top: 25.0, left: 25.0, right: 25.0),
      child: TextField(
        controller: _controller,
        enabled: true,
        keyboardType: TextInputType.multiline,
        maxLines: null,
        minLines: 3,
        decoration: InputDecoration(
          suffixIcon: IconButton(
            onPressed: () => _controller.clear(),
            icon: Icon(
              Icons.clear,
              color: Colors.grey,
            ),
          ),
        ),
        onChanged: (String value) {
          _onChange(value);
        },
      ));
텍스트 읽기, 읽기 위치 재설정, 일시 중지된 "btnSection"
main.dart
Widget _btnSection() {
    return Container(
      padding: EdgeInsets.only(top: 50.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          _buildButtonColumn(
            Colors.green,
            Colors.greenAccent,
            Icons.play_arrow,
            '再生',
            _speak,
          ),
          _buildButtonColumn(
            Colors.red,
            Colors.redAccent,
            Icons.stop,
            'リセット',
            _stop,
          ),
          _buildButtonColumn(
            Colors.blue,
            Colors.blueAccent,
            Icons.pause,
            '停止',
            _pause,
          ),
        ],
      ),
    );
  }
전환 언어
main.dart
Widget _futureBuilder() => FutureBuilder<dynamic>(
      future: _getLanguages(),
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        if (snapshot.hasData) {
          return _languageDropDownSection(snapshot.data);
        } else if (snapshot.hasError) {
          return Text('言語の読み込みに失敗');
        } else
          return Text('読み込み中');
      });
발음 소리, 소리 높이, 속도를 조절하는 "buildSliders"
main.dart
Widget _buildSliders() {
    return Column(
      children: [_volume(), _pitch(), _rate()],
    );
  }

  Widget _volume() {
    return Slider(
        value: volume,
        onChanged: (newVolume) {
          setState(() => volume = newVolume);
        },
        min: 0.0,
        max: 1.0,
        divisions: 10,
        label: "音量: $volume");
  }

  Widget _pitch() {
    return Slider(
      value: pitch,
      onChanged: (newPitch) {
        setState(() => pitch = newPitch);
      },
      min: 0.5,
      max: 2.0,
      divisions: 15,
      label: "声の高さ: $pitch",
      activeColor: Colors.red,
    );
  }

  Widget _rate() {
    return Slider(
      value: rate,
      onChanged: (newRate) {
        setState(() => rate = newRate);
      },
      min: 0.0,
      max: 1.0,
      divisions: 10,
      label: "スピード: $rate",
      activeColor: Colors.green,
    );
  }
대체로 4개의 위젯으로 구성돼 있다.
발음 언어는 기본적으로 영어로 설정되며 "future Builder"에서 일본어를 설정합니다.
다만 일본어, 특히 한자는 읽는 방법이 불안정해 실용성을 원한다면 영어로 읽어 애플리케이션으로 활용하는 것이 좋다.

좋은 웹페이지 즐겨찾기