Flutter数学键盘插件apoapps_math_keyboard的使用

Flutter数学键盘插件apoapps_math_keyboard的使用

math_keyboard 是一个Flutter软件包,允许用户通过一种称为“数学字段”的排版输入字段来编辑数学表达式,并且包含一个完全集成的自定义数学键盘。该插件仅适用于Flutter,不依赖于任何插件或web视图。

特性

  • 使用自定义的屏幕软件键盘编辑数学表达式。
  • 支持物理键盘输入(带有函数和常量的快捷方式)。
  • 同时支持数字模式和表达式模式。
  • 支持高级运算符和三角函数(如sqrtlnsin等)。
  • 支持视图插入(例如,当屏幕键盘覆盖时,可以推动bodyScaffold中向上移动)。
  • 完全集成到焦点树中:可以与常规文本字段、手动FocusNode、制表键等一起工作。
  • 支持自动聚焦。
  • 表单字段支持。
  • 基于区域的十进制分隔符。
  • 可以将TeX字符串转换为数学表达式,反之亦然。

你可以在演示应用中查看所有功能的实际效果。

使用方法

要使用此插件,请遵循安装指南。

基本实现

最简单的集成方式是直接在你的应用程序中添加一个MathField。这与Flutter的TextField非常相似——甚至具有相同的InputDecoration装饰功能!

[@override](/user/override)
Widget build(BuildContext context) {
  return MathField(
    // 不需要参数。
    keyboardType: MathKeyboardType.expression, // 指定键盘类型(表达式或仅数字)。
    variables: const ['x', 'y', 'z'], // 指定用户可以使用的变量(仅在表达式模式下)。
    decoration: const InputDecoration(), // 使用熟悉的`InputDecoration`装饰输入字段。
    onChanged: (String value) {}, // 响应输入字段的变化。
    onSubmitted: (String value) {}, // 响应用户提交输入。
    autofocus: true, // 启用或禁用输入字段的自动聚焦。
  );
}

现在,点击数学字段(或通过焦点树聚焦它)会自动打开数学键盘,并开始接受桌面端的物理键盘输入。

注意:由于Flutter的一个问题(issue #44681),在移动设备上使用物理键盘输入会导致奇怪的行为。

视图插入

数学键盘的一个非常有用的功能是它可以尽可能地模仿常规软件键盘。作为其中的一部分,它会将其大小报告给MediaQuery,形式为view insets。这将无缝集成到像Scaffold这样的小部件以及现有的软件键盘视图插入报告中。

要使用此功能,只需确保你的Scaffold(包含MathField的小部件)被MathKeyboardViewInsets包裹:

[@override](/user/override)
Widget build(BuildContext context) {
  return MathKeyboardViewInsets(
    child: Scaffold(
      // ...
    ),
  );
}

有关更高级用法的详细信息,请参阅文档。

此外,该包还提供了一些方便的函数来检测是否显示了键盘:

  • MathKeyboardViewInsetsQuery.mathKeyboardShowingIn(context),用于报告当前context中是否有数学键盘打开。
  • MathKeyboardViewInsetsQuery.keyboardShowingIn(context),用于报告当前context中是否有任何键盘打开。请注意,此函数实际上也提供了对常规软件键盘的高级功能。

表单支持

为了在Flutter的Form中使用MathField,你可以简单地用MathFormField替换常规的MathField。这类似于TextFormField,并提供了扩展功能。详见后者的高级文档。

[@override](/user/override)
Widget build(BuildContext context) {
  return MathFormField(
    // ...
  );
}

自定义控制器

你可以始终指定一个自定义的MathFieldEditingController。这允许你清除所有输入。务必处理好控制器的生命周期。

class FooState extends State<FooStatefulWidget> {
  late final _controller = MathFieldEditingController();

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _onTapClear() {
    _controller.clear();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MathField(
      controller: _controller,
      decoration: InputDecoration(
        suffix: MouseRegion(
          cursor: MaterialStateMouseCursor.clickable,
          child: GestureDetector(
            onTap: _onTapClear,
            child: const Icon(
              Icons.highlight_remove_rounded,
              color: Colors.grey,
            ),
          ),
        ),
      ),
    );
  }
}

自定义焦点节点

如果你希望自己管理焦点,你可以始终指定自己的FocusNode。这就像任何其他基于焦点的小部件(如TextField)一样工作。注意,即使提供了自定义焦点节点,autofocus仍然有效。

class FooState extends State<FooStatefulWidget> {
  late final _focusNode = FocusNode(debugLabel: 'Foo');

  [@override](/user/override)
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MathField(
      focusNode: _focusNode,
    );
  }
}

十进制分隔符

注意,并非所有国家都使用点.作为小数分隔符(见参考)。如果通过Localizations.localeOf获取的区域设置使用逗号,作为小数分隔符,则数学字段中的分隔符以及键盘上的符号都会相应调整。否则,将使用点.。你可以使用Localizations.override覆盖区域设置。

注意,物理键盘输入总是接受.,

数学表达式

要将数学键盘返回的TeX字符串转换为数学表达式,可以使用提供的TeXParser

final mathExpression = TeXParser(texString).parse();

对于相反的操作,即将数学表达式转换为TeX,可以使用提供的convertMathExpressionToTeXNode

final texNode = convertMathExpressionToTeXNode(expression);

注意,这返回了一个内部的TeXNode格式,你可以将其转换为TeX字符串:

final texString = texNode.buildTexString();

示例代码

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

void main() {
  runApp(const ExampleApp());
}

/// 示例应用,展示如何使用`math_keyboard`包。
class ExampleApp extends StatelessWidget {
  /// 创建一个[ExampleApp]小部件。
  const ExampleApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '数学键盘演示',
      supportedLocales: const [
        Locale('en', 'US'),
        // 提供另一个支持的语言(如“de_DE”)允许在模拟器上切换语言,并看到不同的小数分隔符。只有声明在`supportedLocales`中的区域设置才会由`Localizations.localeOf`返回。因此,如果你想防止使用逗号作为小数分隔符,你不应该提供使用逗号作为小数分隔符的语言。
        Locale('de', 'DE'),
      ],
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
      ],
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: const DemoPage(),
    );
  }
}

/// 小部件,展示如何使用`math_keyboard`包。
class DemoPage extends StatefulWidget {
  /// 创建一个[DemoPage]小部件。
  const DemoPage({Key? key}) : super(key: key);

  [@override](/user/override)
  _DemoPageState createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  var _currentIndex = 0;

  [@override](/user/override)
  Widget build(BuildContext context) {
    Widget child;
    if (_currentIndex == 0) {
      child = const _MathFieldTextFieldExample();
    } else if (_currentIndex == 1) {
      child = const Center(
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Text(
            '当切换到此页面时,数学键盘应自动关闭。',
            textAlign: TextAlign.center,
          ),
        ),
      );
    } else {
      child = const _ClearableAutofocusExample();
    }

    return MathKeyboardViewInsets(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('数学键盘演示'),
        ),
        body: Column(
          children: [
            Expanded(
              child: child,
            ),
            // 我们在这里插入底部导航栏而不是`bottomNavigationBar`参数,以便使其粘附在键盘顶部。
            BottomNavigationBar(
              currentIndex: _currentIndex,
              onTap: (index) {
                setState(() {
                  _currentIndex = index;
                });
              },
              items: const [
                BottomNavigationBarItem(
                  label: '字段',
                  icon: Icon(Icons.text_fields_outlined),
                ),
                BottomNavigationBarItem(
                  label: '空',
                  icon: Icon(Icons.hourglass_empty_outlined),
                ),
                BottomNavigationBarItem(
                  label: '自动聚焦',
                  icon: Icon(Icons.auto_awesome),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

/// 小部件,显示一个包含不同数学字段和比较文本字段的示例列。
class _MathFieldTextFieldExample extends StatelessWidget {
  /// 构造一个[_MathFieldTextFieldExample]小部件。
  const _MathFieldTextFieldExample({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SafeArea(
      child: ListView(
        children: [
          const Padding(
            padding: EdgeInsets.all(16),
            child: TextField(),
          ),
          Padding(
            padding: const EdgeInsets.all(16),
            child: MathField(
              variables: const ['a', 's', 'c'],
              onChanged: (value) {
                String expression;
                try {
                  expression = '${TeXParser(value).parse()}';
                } catch (_) {
                  expression = '无效输入';
                }

                print('输入表达式: $value\n'
                    '转换后的表达式: $expression');
              },
            ),
          ),
          const Padding(
            padding: EdgeInsets.all(16),
            child: MathField(
              keyboardType: MathKeyboardType.numberOnly,
            ),
          ),
        ],
      ),
    );
  }
}

/// 小部件,展示一个可以由外部清除并自动接收焦点的示例数学字段。
class _ClearableAutofocusExample extends StatefulWidget {
  /// 构造一个[_ClearableAutofocusExample]小部件。
  const _ClearableAutofocusExample({Key? key}) : super(key: key);

  [@override](/user/override)
  _ClearableAutofocusExampleState createState() => _ClearableAutofocusExampleState();
}

class _ClearableAutofocusExampleState extends State<_ClearableAutofocusExample> {
  late final _controller = MathFieldEditingController();

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SafeArea(
      child: ListView(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: MathField(
              autofocus: true,
              controller: _controller,
              decoration: InputDecoration(
                suffix: MouseRegion(
                  cursor: MaterialStateMouseCursor.clickable,
                  child: GestureDetector(
                    onTap: _controller.clear,
                    child: const Icon(
                      Icons.highlight_remove_rounded,
                      color: Colors.grey,
                    ),
                  ),
                ),
              ),
            ),
          ),
          const Padding(
            padding: EdgeInsets.all(16),
            child: Text(
              '此选项卡中的数学字段应自动接收焦点。',
              textAlign: TextAlign.center,
            ),
          ),
        ],
      ),
    );
  }
}

更多关于Flutter数学键盘插件apoapps_math_keyboard的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数学键盘插件apoapps_math_keyboard的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用apoapps_math_keyboard插件的一个简单示例。这个插件允许你在应用中集成一个数学键盘,非常适合需要用户输入数学表达式或公式的场景。

首先,确保你已经在pubspec.yaml文件中添加了apoapps_math_keyboard依赖:

dependencies:
  flutter:
    sdk: flutter
  apoapps_math_keyboard: ^最新版本号 # 请替换为实际最新版本号

然后运行flutter pub get来安装依赖。

接下来,你可以在你的Flutter项目中创建一个使用数学键盘的页面。以下是一个简单的示例代码:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Math Keyboard Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MathKeyboardDemoPage(),
    );
  }
}

class MathKeyboardDemoPage extends StatefulWidget {
  @override
  _MathKeyboardDemoPageState createState() => _MathKeyboardDemoPageState();
}

class _MathKeyboardDemoPageState extends State<MathKeyboardDemoPage> {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Math Keyboard Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Enter Math Expression',
              ),
              keyboardType: TextInputType.multiline,
              maxLines: null,
              expands: true,
            ),
            SizedBox(height: 16.0),
            ElevatedButton(
              onPressed: () {
                showMathKeyboard(
                  context: context,
                  textController: _controller,
                  onSubmit: (String expression) {
                    // 处理用户提交的表达式
                    print('User submitted: $expression');
                    // 可以关闭键盘或进行其他操作
                  },
                );
              },
              child: Text('Open Math Keyboard'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个TextField用于显示用户输入的数学表达式,以及一个按钮用于打开数学键盘。当用户点击按钮时,将调用showMathKeyboard函数来显示数学键盘。用户输入的表达式将通过onSubmit回调进行处理。

请注意,showMathKeyboard函数是apoapps_math_keyboard插件提供的一个顶级函数,用于显示数学键盘。这个函数需要传入上下文(context)、文本控制器(textController)以及一个提交回调(onSubmit)。

确保你已经正确导入了apoapps_math_keyboard包,并根据需要调整代码以适应你的应用逻辑。这个示例应该可以帮助你快速上手使用apoapps_math_keyboard插件。

回到顶部