Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Scribe Android handwriting text input #148784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Nov 21, 2024
Merged

Conversation

justinmc
Copy link
Contributor

@justinmc justinmc commented May 21, 2024

Enables the Scribe feature, or Android stylus handwriting text input.

scribe

This PR only implements basic handwriting input. Other features will be done in subsequent PRs:

I created and fixed issue about stylus hovering while working on this: #148810

Original PR for iOS Scribble, the iOS version of this feature: #75472
FYI @fbcouch

Depends on flutter/engine#52943 (merged).

Fixes #115607

Example code I'm using to test this feature (but any TextField works)
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final FocusNode _focusNode1 = FocusNode();
  final FocusNode _focusNode2 = FocusNode();
  final FocusNode _focusNode3 = FocusNode();
  final TextEditingController _controller3 = TextEditingController(
    text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Scribe demo'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 74.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextField(
                focusNode: _focusNode1,
                autofocus: false,
              ),
              TextField(
                focusNode: _focusNode2,
              ),
              TextField(
                focusNode: _focusNode3,
                minLines: 4,
                maxLines: 4,
                controller: _controller3,
              ),
              TextButton(
                onPressed: () {
                  _focusNode1.unfocus();
                  _focusNode2.unfocus();
                  _focusNode3.unfocus();
                },
                child: const Text('Unfocus'),
              ),
              TextButton(
                onPressed: () {
                  _focusNode1.requestFocus();
                  SchedulerBinding.instance.addPostFrameCallback((Duration _) {
                    SystemChannels.textInput.invokeMethod('TextInput.hide');
                  });
                },
                child: const Text('Focus 1'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

@justinmc justinmc self-assigned this May 21, 2024
@github-actions github-actions bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. labels May 21, 2024
@vistaar-mlopes
Copy link

vistaar-mlopes commented May 29, 2024

hi. @justinmc I have Samsung galaxy s9 ultra tab, I wanted to implement the above feature. I went through above PR and till it gets approved, can you please suggest a temporary solution that I can implement using native android?
I found your comment on using 2 native methods :
"Android's InputMethodManager class already has support for stylus handwriting. We'll need to tie these into the framework similarly to how it's done for iOS.

Some relevant methods:

1.InputMethodManager.startStylusHandwriting
2.View.setAutoHandwritingEnabled"
can I implement method channnel and access this native features?

Thanks.

@justinmc
Copy link
Contributor Author

@vistaar-mlopes I'm not sure whether you could get this working by writing a plugin. The bare minimum thing you'd need to do is to call startStylusHandwriting on the InputMethodManager. If you couldn't do that with a plugin, you would need to build a custom engine and framework.

By the way, if you end up trying this feature out using your Samsung device, could you let me know if it works? I'm assuming/hoping that other Android devices will work the same as what I'm using (Pixel Tablet).

@github-actions github-actions bot added f: material design flutter/packages/flutter/material repository. f: cupertino flutter/packages/flutter/cupertino repository labels Jun 3, 2024

@override
Widget build(BuildContext context) {
return Listener(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: I can listen directly to the global pointer binding to avoid the problem with clipping out events that I want.

@vistaar-mlopes
Copy link

@justinmc Recently I got a Lenovo tab. I tried to scribble on the text field and it worked. I typed hello and it was there in the text field. whereas I tried the same on the Samsung tab with S-pen it didn't work. Now I'm not sure where the issue is. Is it a flutter issue or is it device dependent?

@justinmc
Copy link
Contributor Author

@vistaar-mlopes Interesting! You tried this on master, not on this branch, right? I'm not sure how those other devices do handwriting. Maybe they have their own implementations since I believe Android hasn't supported this officially until recently.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Scribble tests were just moved out of editable_text_test.dart.

@justinmc justinmc force-pushed the scribe branch 2 times, most recently from e3bb6f5 to 9511cf2 Compare October 22, 2024 19:47
@justinmc
Copy link
Contributor Author

@vistaar-mlopes If you could try this branch out with your Samsung and Lenovo tablets I'd be interested to see if it solves your problem! Now no engine change is needed, just this framework branch.

@justinmc justinmc marked this pull request as ready for review October 23, 2024 17:56
@justinmc justinmc changed the title (WIP) Scribe Android handwriting text input Scribe Android handwriting text input Oct 23, 2024
Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(looked at widgets and services)

/// built-in text fields support handwriting input.
/// * [SystemChannels.scribe], which is the [MethodChannel] used by this
/// class, and which has a list of the methods that this class handles.
class Scribe {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final class since everything seems to be static.

/// * [SystemChannels.scribe], which is the [MethodChannel] used by this
/// class, and which has a list of the methods that this class handles.
class Scribe {
Scribe._() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who calls this private constructor?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also how's linter not complaining if this is not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this is leftover from when I was handling gesture calls, before I moved it to a separate branch. I'll just remove all this. Not sure about the linter! Maybe the linter can't handle constructors?

/// A convenience method to check if the device currently supports Scribe
/// stylus handwriting input.
///
/// Call this before calling [startStylusHandwriting] to make sure it's
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often does the caller have to check if the feature is available? Can I assume the return value won't change while the app is running?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It must be called each time before calling startStylusHandwriting. I'll update the docs.

}

/// Returns true if the InputMethodManager supports Scribe stylus handwriting
/// input.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do false and null mean the same thing?

Copy link
Contributor Author

@justinmc justinmc Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nullability is a limitation of invokeMethod (docs don't explain it super well either but maybe it's just that the codec can't enforce non-nullability?). I will explain in the docs here.


/// Tell Android to begin receiving stylus handwriting input.
///
/// This is typically called after detecting a [PointerDownEvent] indicating
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the Android documentation this can't be called when there is no active input connection it seems (maybe I'm reading the doc wrong, it says "This will always be preceded by onStartInput(android.view.inputmethod.EditorInfo, boolean) for the EditorInfo and InputConnection for which stylus handwriting is being requested")?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I tried calling it with no active TextInputConnection and it was ignored. I'll update the docs to explain this.


/// Returns a new Rect whose size has changed by the given padding while
/// remaining centered.
static Rect _pad(Rect rect, EdgeInsets padding) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// [Rect] in global coordinates.
static Rect _localToGlobalRect(Rect rect, RenderBox renderBox) {
return Rect.fromPoints(
renderBox.localToGlobal(rect.topLeft),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

void initState() {
super.initState();
if (widget.enabled) {
GestureBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What prevents 2 overlapping text fields from competing for stylus point down events? Also, the stylus hitting an invisible text field (in an inactive route, for example) would also trigger scribble it seems?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why can't this be a regular gesture recognizer / detector? Is it because of the padding?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will follow up on this one on Monday.

What prevents 2 overlapping text fields from competing for stylus point down events?

Good catch, I just tried this out and it looks like the behavior to the user is that the field lowest in the tree will win if I write directly in between the two fields, where their paddings are overlapping. If I get really accurate with the stylus I can write just in the bottom of the upper field but the lower field will receive the text, but for the most part the behavior is probably fine in this ambiguous situation.

However, both fields do receive the event. I'll need to follow up and do something to prevent that.

3 TextFields right next to each other
child: Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    TextField(
      controller: controller,
      stylusHandwritingEnabled: true,
      decoration: InputDecoration(
        border: OutlineInputBorder(),
      ),
    ),
    TextField(
      stylusHandwritingEnabled: true,
      decoration: InputDecoration(
        border: OutlineInputBorder(),
      ),
    ),
    TextField(
      stylusHandwritingEnabled: true,
      decoration: InputDecoration(
        border: OutlineInputBorder(),
      ),
    ),
  ],
),

Also, the stylus hitting an invisible text field (in an inactive route, for example) would also trigger scribble it seems?

Should I check ModalRoute.of(context) or something like that? Anything else I should check?

Also why can't this be a regular gesture recognizer / detector? Is it because of the padding?

Yes, this was my solution to needing to listen to an area outside of the size of the field.

return _ScribbleFocusable(
focusNode: focusNode,
editableKey: editableKey,
enabled: enabled,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enabled is always true here right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and it is more readable if I pass true here 👍

}

// A stylus event that starts on a selection handle does not start
// handwriting, it moves the handle.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish this logic lives in the TextSelectionHandle (or whatever it is called) since we have different handle classes for android and iOS (I think)?

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding hit-testing, how important is it to support _handwritingPadding for how?

Can this be implemented using gesture recognizers, by either:

So we don't have to replicate the hit testing logic and z-index hittest behavior logic?

/// built-in text fields support handwriting input.
/// * [SystemChannels.scribe], which is the [MethodChannel] used by this
/// class, and which has a list of the methods that this class handles.
final class Scribe {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: abstract final class to indicate the class is just a namespace?

///
/// The return type is nullable due to the way
/// [MethodChannel.invokeMethod](https://main-api.flutter.dev/flutter/services/MethodChannel/invokeMethod.html)
/// works, but a successful call will never resolve to `null`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to do .then<bool>((bool? value) => value ?? false)

or .then<bool>((bool? value) => value ?? throw SomeError) then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I should do this checking and not the user.

/// Returns true if the InputMethodManager supports Scribe stylus handwriting
/// input, false otherwise.
///
/// Call this each time before calling [startStylusHandwriting] to make sure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the case shouldn't this API be incorporated into startStylusHandwriting then? Since they are always paired?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like it should, but that's not the way that Android designed their API for whatever reason. I want to provide an accurate reproduction of the Android API in case our users need it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to 100% match the Android API should we call this classAndroidScribe then? Also are there any android handwriting overview in their docs we could link in the class API docs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just made an attempt at renaming this but got halfway through and started having second thoughts. Following this naming, Scribble should be called iOSScribble, and the method channel names don't quite match (Scribe.x instead of AndroidScribe.x).

I'll make sure the docs are clear about the direct connection to Android's APIs. I found a good page to link to like you suggested: https://developer.android.com/develop/ui/views/touch-and-input/stylus-input/stylus-input-in-text-fields

/// which is the corresponding API on Android.
/// * [EditableText.stylusHandwritingEnabled], which controls whether
/// Flutter's built-in text fields support handwriting input.
static Future<void> startStylusHandwriting() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the IME is responsible for determining when to stop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It seems like it's a timeout from the last screen touch.

///
/// The following methods are defined for this channel:
///
/// * `Scribe.startStylusHandwriting`: Indicates that stylus input has been
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: [] instead of ``?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why I didn't do that in the first place...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it should be tick marks, because this refers to the platform channel method, not the method on the Scribe class.

@@ -1975,6 +2001,9 @@ class EditableText extends StatefulWidget {
/// {@macro flutter.widgets.magnifier.intro}
final TextMagnifierConfiguration magnifierConfiguration;

/// The default value for [stylusHandwritingEnabled].
static const bool defaultStylusHandwritingEnabled = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the default exposed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to reference it from other files that also need the same default, instead of duplicating it.


class _ScribeState extends State<_Scribe> {
// The handwriting bounds padding of EditText in Android API 34.
static const EdgeInsets _handwritingPadding = EdgeInsets.symmetric(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a fixed size or just the size of the input decorator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the fixed amount of padding that native Android uses to detect Scribe events outside of the field.

@auto-submit auto-submit bot added this pull request to the merge queue Nov 21, 2024
Merged via the queue into flutter:master with commit b473698 Nov 21, 2024
79 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
@justinmc justinmc deleted the scribe branch November 21, 2024 18:26
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Nov 21, 2024
Roll Flutter from 8536b96ebb3e to 93d772c5cdd8 (37 revisions)

flutter/flutter@8536b96...93d772c

2024-11-21 [email protected] Added additional logging to `_listCoreDevices` (flutter/flutter#159275)
2024-11-21 [email protected] Roll Flutter Engine from 78b87f3fe023 to d1a08064e193 (1 revision) (flutter/flutter#159280)
2024-11-21 [email protected] Shut down DevTools and DDS processes if flutter_tools is killed by a signal (flutter/flutter#159238)
2024-11-21 [email protected] Remove `RepaintBoundary` that is no longer needed. (flutter/flutter#159232)
2024-11-21 [email protected] Try a speculative fix for Gradle OOMs. (flutter/flutter#159234)
2024-11-21 [email protected] On-device Widget Inspector button exits widget selection (flutter/flutter#158219)
2024-11-21 [email protected] Roll Flutter Engine from 523d381893c8 to 78b87f3fe023 (2 revisions) (flutter/flutter#159270)
2024-11-21 [email protected] Remove `firebase_abstract_method_smoke_test` (flutter/flutter#159145)
2024-11-21 [email protected] Roll Packages from e95f6d8 to 913b99e (7 revisions) (flutter/flutter#159268)
2024-11-21 [email protected] Roll Flutter Engine from 69c325513a65 to 523d381893c8 (3 revisions) (flutter/flutter#159263)
2024-11-21 [email protected] [flutter_tools] opt iOS/macOS apps out of Metal API validation via migrator, update templates in repo. (flutter/flutter#159228)
2024-11-21 [email protected] Scribe Android handwriting text input (flutter/flutter#148784)
2024-11-21 [email protected] Terminate non-detached test devices on `flutter run` completion (flutter/flutter#159170)
2024-11-21 [email protected] Un-skip tests that use `flutter build apk`. (flutter/flutter#159231)
2024-11-20 [email protected] Roll Flutter Engine from 2d32cf3a7971 to 69c325513a65 (1 revision) (flutter/flutter#159229)
2024-11-20 [email protected] Roll Flutter Engine from 3828681d1f86 to 2d32cf3a7971 (3 revisions) (flutter/flutter#159226)
2024-11-20 [email protected] Roll Flutter Engine from 3245c8976976 to 3828681d1f86 (1 revision) (flutter/flutter#159217)
2024-11-20 [email protected] Add `--dry-run` to `dev/bots/test.dart`. (flutter/flutter#158956)
2024-11-20 [email protected] Add docs for setting up Android Studio to auto format Kotlin code (flutter/flutter#159209)
2024-11-20 [email protected] Roll Flutter Engine from 80d77505fdde to 3245c8976976 (1 revision) (flutter/flutter#159214)
2024-11-20 [email protected] Fix: The enableFeedback property of InkWell cannot be set to a nullabâ�¦ (flutter/flutter#158907)
2024-11-20 [email protected] Roll Flutter Engine from 3f19207e820e to 80d77505fdde (1 revision) (flutter/flutter#159210)
2024-11-20 [email protected] Roll Packages from fc4adc7 to e95f6d8 (6 revisions) (flutter/flutter#159201)
2024-11-20 [email protected] Remove dependency on [Target] and instead operate on [Architecture] (flutter/flutter#159196)
2024-11-20 [email protected] Fix git command in Quality-Assurance.md (flutter/flutter#155146)
2024-11-20 [email protected] Roll Flutter Engine from 7eb87547cbc6 to 3f19207e820e (4 revisions) (flutter/flutter#159176)
2024-11-20 [email protected] Make `runner` non-nullable as it always is. (flutter/flutter#159156)
2024-11-19 [email protected] Update Material 3 `CircularProgressIndicator` for new visual style (flutter/flutter#158104)
2024-11-19 [email protected] Roll Flutter Engine from 4ff696b555dc to 7eb87547cbc6 (3 revisions) (flutter/flutter#159168)
2024-11-19 [email protected] Add platform-android label for all flutter_tools *android* files (flutter/flutter#159166)
2024-11-19 [email protected] Roll Flutter Engine from d5820a638885 to 4ff696b555dc (1 revision) (flutter/flutter#159164)
2024-11-19 [email protected] Reland Add UI Benchmarks (flutter/flutter#153368)
2024-11-19 [email protected] fix lint usage of `task` inside `resolve_dependecies.gradle` file (flutter/flutter#158022)
2024-11-19 [email protected] Roll Flutter Engine from cff1e751f853 to d5820a638885 (5 revisions) (flutter/flutter#159155)
2024-11-19 [email protected] Removing redundant backticks in `flutter\packages\flutter_tools\gradle\gradle.kts` (flutter/flutter#159051)
2024-11-19 [email protected] Fixes initial validation with AutovalidateMode.always on first build (flutter/flutter#156708)
2024-11-19 [email protected] Introduce new Material 3 `Slider` shapes (flutter/flutter#152237)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

...
@reidbaker reidbaker mentioned this pull request Dec 13, 2024
11 tasks
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 12, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: text input Entering text in a text field or keyboard related problems f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support stylus handwriting input on Android
4 participants