🐦

Flutter: 正規表現を判定するアプリ作ってみた

6 min read

以前Flutterで正規表現を使った機能を実装する機会があり、やったことのまとめも兼ねてアプリ作ってみました。

作ったもの

https://youtu.be/XFlVGOm8mi0

inputに判定対象の文字列を入力し、regexに正規表現を入力すると、resultに判定結果が出力されます。

https://github.com/moimoi-prog/regex_test

ソースコードの解説

状態管理はChangeNotifier + Providerを使用しています。
stateを生成し

top_page_state.dart
import 'package:flutter/cupertino.dart';

class TopPageState extends ChangeNotifier {
  String inputText = '';
  String regexText = '';

  void changeInput(String text) {
    inputText = text;
    notifyListeners();
  }

  void changeRegex(String text) {
    regexText = text;
    notifyListeners();
  }
}

Providerで囲って使用できるようにし、Consumerを使って変更があった際にリビルドさせます。

top_page.dart
import 'package:flutter/material.dart';
import 'package:regex_test/state/top_page_state.dart';
import 'package:regex_test/widget/input_field.dart';
import 'package:regex_test/widget/output_field.dart';
import 'package:regex_test/widget/regex_text.dart';
import 'package:provider/provider.dart';

class TopPage extends StatelessWidget {
  const TopPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<TopPageState>(
      create: (BuildContext context) => TopPageState(),
      child: GestureDetector(
        onTap: () {
          final FocusScopeNode currentScope = FocusScope.of(context);
          if (!currentScope.hasPrimaryFocus && currentScope.hasFocus) {
            FocusManager.instance.primaryFocus!.unfocus();
          }
        },
        child: Scaffold(
          appBar: AppBar(
            title: const Text('Regex Checker'),
          ),
          body: Container(
            margin: const EdgeInsets.all(16),
            child: SingleChildScrollView(
              child: Consumer<TopPageState>(
                builder: (context, state, _) {
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      InputField(
                        label: 'input',
                        onChanged: (String text) {
                          state.changeInput(text);
                        },
                      ),
                      const SizedBox(
                        height: 8,
                      ),
                      InputField(
                        label: 'regex',
                        onChanged: (String text) {
                          state.changeRegex(text);
                        },
                      ),
                      const SizedBox(
                        height: 12,
                      ),
                      const Align(
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'result',
                          style: TextStyle(
                            color: Colors.grey,
                          ),
                        ),
                      ),
                      OutputField(
                        child: RegexText(
                          text: state.inputText,
                          regex: state.regexText,
                        ),
                      ),
                    ],
                  );
                },
              ),
            ),
          ),
        ),
      ),
    );
  }
}

正規表現を判定し、結果を返すWidgetはこちらです。

regex_text.dart
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';

class RegexText extends StatelessWidget {
  const RegexText({
    Key? key,
    required this.text,
    required this.regex,
  }) : super(key: key);

  final String text;
  final String regex;

  
  Widget build(BuildContext context) {
    final List<TextSpan> textList = <TextSpan>[];

    try {
      // 正規表現で判定
      final RegExp stringRegex = RegExp(
        regex,
        multiLine: true,
      );

      text.splitMapJoin(
        stringRegex,
        onMatch: (Match m) {
	  // マッチした箇所の処理
          textList.add(
            TextSpan(
              text: m[0],
              style: const TextStyle(
                color: Colors.blueAccent,
                fontSize: 14,
                fontWeight: FontWeight.bold,
              ),
            ),
          );
          return '';
        },
	
	// マッチしていない箇所の処理
        onNonMatch: (String span) {
          textList.add(
            TextSpan(
              text: span,
              style: const TextStyle(
                color: Colors.black,
                fontSize: 14,
                fontWeight: FontWeight.normal,
              ),
            ),
          );
          return '';
        },
      );
    } catch (e) {
      // regexが入力途中の場合エラーが返却されることがあるので、ここでキャッチ
      textList.add(
        const TextSpan(
          text: '正規表現が不正です',
          style: TextStyle(
            color: Colors.red,
            fontSize: 14,
            fontWeight: FontWeight.normal,
          ),
        ),
      );
    }

    return RichText(
      text: TextSpan(
        children: textList,
      ),
    );
  }
}

キーボード入力する際にテキストボックス以外をタップした場合フォーカスを外れるようにするとUX的に良いです。

GestureDetector(
  onTap: () {
   final FocusScopeNode currentScope = FocusScope.of(context);
    if (!currentScope.hasPrimaryFocus && currentScope.hasFocus) {
     FocusManager.instance.primaryFocus!.unfocus();
    }
    },
  child: Scaffold(
   ・・・
  ),
),

参考文献

https://zenn.dev/pressedkonbu/articles/copy-paste-text-form-field
https://qiita.com/tsukushibito/items/d7fde1d998a30292add7

Discussion

ログインするとコメントできます