tts autoPlay

    Future<void> _autoPlay() async {
        _isAuto = true;
        _autoSpeak();

        await showDialog (
            context: context,
            barrierDismissible: false,
            builder: (BuildContext context) {
                return SimpleDialog(
                    contentPadding: EdgeInsets.only(bottom: 16),
                    title: Text('${_title} ${_cn}:${_pIndex+1}', textAlign: TextAlign.center,),
                    children: <Widget>[
                        _verse_ck(_verses[_cn-1].vs[_pIndex], 'kk'),
                        GestureDetector(
                            child: Container(
                                padding: EdgeInsets.all(16.0),
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    color: Colors.grey[200],
                                ),
                                child: Icon(Icons.pause, color: Colors.grey,),
                            ),
                            onTap: () {
                                _isAuto = false;
                                _stop();
                                Navigator.pop(context);
                            }
                        )
                    ],
                );
            }
        );
    }

preloader

    void _loadVerses(int bn) async {
        var dialog = showDialog(
            context: context,
            barrierDismissible: false,
            builder: (BuildContext context) {
                return AlertDialog(
                    titlePadding: EdgeInsets.all(32),
                    title:  Text(_books[bn - 1].kr, textAlign: TextAlign.center),
                    contentPadding: EdgeInsets.only(bottom: 32),
                    content: Text('로딩중', textAlign: TextAlign.center),
                );
            }
        );
        String jsonVerses = await rootBundle.loadString('assets/verses/${bn}.json');
        Iterable list = json.decode(jsonVerses);
        setState(() {
            _bn = bn;
            _cn = 1;
            _title = _books[bn-1].kr;
            _verses = list.map((model) => VersesList.fromJson(model)).toList();
            _chapters = List<String>.generate(_books[bn-1].cc, (int index) => '제${index+1}장');
            _controller = TabController(length: _chapters.length, vsync: this)..addListener(_controllerHandler);
            sharedPreferences.setInt("bn", bn);
            _isLoading = false;
        });
        Navigator.of(context, rootNavigator: true).pop('dialog');
    }

_scroller.animateTo  좌표지정

scroll은 어차피 rendering 된 만큼 상태에서 상대 좌표가 된다.
결국 절대 좌표가 아니라는 것!

따라서 key 값으로 position을 구하게 되면 보이지 않는 Container의 position은 구할 수 없게된다.
생성 될 때 좌표 값을 저장해 두는 방법으로 처리해야 한다.

개발을 오래하다보면

잘 못하게 된다.

늘 하던 습관을 버리기 정말 어렵다.

새로운 방식이 너무 귀찮게 느껴져서 피하게 된다.

바둑기사도 이와 비슷한 면이 있는 듯 하다.

AudioPlayer

    GestureDetector _play(int id, String lang) {

        return GestureDetector(
            child: Container(
                padding: EdgeInsets.fromLTRB(16, _size*.8 , 16, 16 ),
                child: !_isPlay ? Icon(Icons.volume_up, color: Colors.grey[300]) : Icon(Icons.volume_off, color: Colors.grey[300])
            ),
            onTap: () {
                if( _isPlay ) {
                    setState(() {
                        _isPlay = false;
                    });
                    audioPlayer.stop();
                } else {
                    setState(() {
                        _isPlay = true;
                    });
                    audioPlayer.play('<URL>');
                }

            }
        );
    }
    @override
    void initState() {
        super.initState();
        audioPlayer = new AudioPlayer();
        audioPlayer.onPlayerCompletion.listen((event) {
            setState(() {
                _isPlay = false;
            });
        });
    }
    play(String url) async {
        await audioPlayer.play(url);
    }

ruby rt

    Container _rt(String word) {
        List<String> s = word.split( RegExp("<rt>|<\/rt>") );
        return Container(
            child: Stack(
                alignment:  Alignment.topCenter,
                children: <Widget>[
                    Container(
                        child: Text( s[1], textAlign: TextAlign.center,  style: TextStyle(fontSize: _size*.6, color: Colors.grey ) ),
                    ),
                    Container(
                        padding: EdgeInsets.only(top: _size*.6),
                        child: Text( s[0],  style: TextStyle(fontSize: _size, ) )
                    ),
                ]
            )
        );
    }
    Widget _verse_ck(String text, bool center, bool play) {
        text = text.replaceAll( "<" , "<");
        text = text.replaceAll( ">" , ">");

        List<String> split = text.split( RegExp("<ruby>|<\/ruby>") );
        List<Widget> wrap = [];

        for (var s in split) {
            if (s == '')
                continue;
            if ( s.indexOf( "<rt>" ) != -1) {
                if (s.indexOf( "<rp>" ) != -1)
                    wrap.add( _rp(s) );
                else if ( s.indexOf( "<u>" ) != -1)
                    wrap.add( _rtu(s) );
                else
                    wrap.add( _rt(s) );
            } else {
                if ( s.indexOf( " " ) != -1) {
                    List<String> space = s.split( RegExp(" ") );
                    for (var p in space) {
                        if (p == '')
                            continue;
                        wrap.add( _word(p) );
                        wrap.add( _space() );
                    }
                } else {
                    wrap.add( _word(s) );
                }
            }
        }

        if (play)
            wrap.add( _play() );

        if (center)
            return Container (
                padding: EdgeInsets.fromLTRB(16, 32, 16, 32),
                child: Center(
                    child: Wrap(
                        children: wrap
                    )
                )
            );
        else
            return Container (
                padding: EdgeInsets.fromLTRB(16, 32, 16, 32),
                child: Wrap(
                    children: wrap
                )
            );

    }
    Widget _buildVerse(Verse vs ) {
        List<Widget> widgets = [];
        List<String> vss = ['kr'];

        widgets.add( _verse_vn(vs.vn) );

        for( var v in vss) {
            if (v == 'tc') {
                if (vs.ts != '')
                    widgets.add( _verse_ck(vs.ts, true, false) );
                widgets.add( _verse_ck(vs.tc, false, false) );
            }
            if (v == 'kr') {
                if (vs.ks != '')
                    widgets.add( _verse_ck(vs.ks, true, false) );
                widgets.add( _verse_ck(vs.kr, false, true) );
            }
            if (v == 'sc')
                widgets.add( _verse_ck(vs.sc, false, true) );

            if (v == 'en') {
                if (vs.es != '')
                    widgets.add( _verse_es(vs.es) );
                widgets.add( _verse_en(vs.en) );
            }
        }

        return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: widgets
        );
    }

_verse_kr

    Widget _buildVerse(Verse vs ) {
        return Wrap(
            children: <Widget>[
                _verse_kr( vs.kr ),
            ],
        );
    }

    Widget _verse_kr(String kr ) {
        List<Widget> words = [];
        List<String> split = kr.split( RegExp("<ruby>|<\/ruby>") );
        for (var s in split) {
            if ( s.indexOf( "<rt>" ) != -1) {
                List<String> rt = s.split( RegExp("<rt>|<\/rt>") );
                words.add( _makeWord(rt[1], rt[0]) );
            } else {
                if ( s.indexOf( " " ) != -1 ) {
                    List<String> space = s.split( " " );
                    for (var w in space) {
                        words.add( _makeWord('', w ) );
                        words.add( Text(' ') );
                    }
                } else {
                    words.add( _makeWord('', s ) );
                }
            }
        }
        words.add( Container(
            padding: EdgeInsets.fromLTRB(8, _size*.6, 0, 0),
            child: Icon(Icons.volume_up, color: Colors.grey[300])
            )
        );
        return Container (
            padding: EdgeInsets.all(16),
            child: Wrap(
                children: words,
            )
        );
    }

    Widget _makeWord(String sup, String text ){
        return Column(
            children: <Widget>[
                Container(
                    height: _size * .6,
                    child: Text(sup, textAlign: TextAlign.center, style: TextStyle( fontSize: _size*.6)),
                ),
                Container(
                    child: Text(text, textAlign: TextAlign.center, style: TextStyle( fontSize: _size) ),
                ),
            ],
        );
    }

버리기

다른 것을 얻으려면 우선 가진 것부터 버려야 할 것이다.