@@ -393,79 +393,7 @@ class _ContentInput extends StatefulWidget {
393
393
State <_ContentInput > createState () => _ContentInputState ();
394
394
}
395
395
396
- class _ContentInputState extends State <_ContentInput > with WidgetsBindingObserver {
397
- @override
398
- void initState () {
399
- super .initState ();
400
- widget.controller.content.addListener (_contentChanged);
401
- widget.controller.contentFocusNode.addListener (_focusChanged);
402
- WidgetsBinding .instance.addObserver (this );
403
- }
404
-
405
- @override
406
- void didUpdateWidget (covariant _ContentInput oldWidget) {
407
- super .didUpdateWidget (oldWidget);
408
- if (widget.controller != oldWidget.controller) {
409
- oldWidget.controller.content.removeListener (_contentChanged);
410
- widget.controller.content.addListener (_contentChanged);
411
- oldWidget.controller.contentFocusNode.removeListener (_focusChanged);
412
- widget.controller.contentFocusNode.addListener (_focusChanged);
413
- }
414
- }
415
-
416
- @override
417
- void dispose () {
418
- widget.controller.content.removeListener (_contentChanged);
419
- widget.controller.contentFocusNode.removeListener (_focusChanged);
420
- WidgetsBinding .instance.removeObserver (this );
421
- super .dispose ();
422
- }
423
-
424
- void _contentChanged () {
425
- final store = PerAccountStoreWidget .of (context);
426
- (widget.controller.content.text.isEmpty)
427
- ? store.typingNotifier.stoppedComposing ()
428
- : store.typingNotifier.keystroke (widget.destination);
429
- }
430
-
431
- void _focusChanged () {
432
- if (widget.controller.contentFocusNode.hasFocus) {
433
- // Content input getting focus doesn't necessarily mean that
434
- // the user started typing, so do nothing.
435
- return ;
436
- }
437
- final store = PerAccountStoreWidget .of (context);
438
- store.typingNotifier.stoppedComposing ();
439
- }
440
-
441
- @override
442
- void didChangeAppLifecycleState (AppLifecycleState state) {
443
- switch (state) {
444
- case AppLifecycleState .hidden:
445
- case AppLifecycleState .paused:
446
- case AppLifecycleState .detached:
447
- // Transition to either [hidden] or [paused] signals that
448
- // > [the] application is not currently visible to the user, and not
449
- // > responding to user input.
450
- //
451
- // When transitioning to [detached], the compose box can't exist:
452
- // > The application defaults to this state before it initializes, and
453
- // > can be in this state (applicable on Android, iOS, and web) after
454
- // > all views have been detached.
455
- //
456
- // For all these states, we can conclude that the user is not
457
- // composing a message.
458
- final store = PerAccountStoreWidget .of (context);
459
- store.typingNotifier.stoppedComposing ();
460
- case AppLifecycleState .inactive:
461
- // > At least one view of the application is visible, but none have
462
- // > input focus. The application is otherwise running normally.
463
- // For example, we expect this state when the user is selecting a file
464
- // to upload.
465
- case AppLifecycleState .resumed:
466
- }
467
- }
468
-
396
+ class _ContentInputState extends State <_ContentInput > with WidgetsBindingObserver , _TypingNotifierMixin {
469
397
static double maxHeight (BuildContext context) {
470
398
final clampingTextScaler = MediaQuery .textScalerOf (context)
471
399
.clamp (maxScaleFactor: 1.5 );
@@ -540,6 +468,80 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve
540
468
}
541
469
}
542
470
471
+ mixin _TypingNotifierMixin on State <_ContentInput >, WidgetsBindingObserver {
472
+ @override
473
+ void initState () {
474
+ super .initState ();
475
+ widget.controller.content.addListener (_contentChanged);
476
+ widget.controller.contentFocusNode.addListener (_focusChanged);
477
+ WidgetsBinding .instance.addObserver (this );
478
+ }
479
+
480
+ @override
481
+ void didUpdateWidget (covariant _ContentInput oldWidget) {
482
+ super .didUpdateWidget (oldWidget);
483
+ if (widget.controller != oldWidget.controller) {
484
+ oldWidget.controller.content.removeListener (_contentChanged);
485
+ widget.controller.content.addListener (_contentChanged);
486
+ oldWidget.controller.contentFocusNode.removeListener (_focusChanged);
487
+ widget.controller.contentFocusNode.addListener (_focusChanged);
488
+ }
489
+ }
490
+
491
+ @override
492
+ void dispose () {
493
+ widget.controller.content.removeListener (_contentChanged);
494
+ widget.controller.contentFocusNode.removeListener (_focusChanged);
495
+ WidgetsBinding .instance.removeObserver (this );
496
+ super .dispose ();
497
+ }
498
+
499
+ void _contentChanged () {
500
+ final store = PerAccountStoreWidget .of (context);
501
+ (widget.controller.content.text.isEmpty)
502
+ ? store.typingNotifier.stoppedComposing ()
503
+ : store.typingNotifier.keystroke (widget.destination);
504
+ }
505
+
506
+ void _focusChanged () {
507
+ if (widget.controller.contentFocusNode.hasFocus) {
508
+ // Content input getting focus doesn't necessarily mean that
509
+ // the user started typing, so do nothing.
510
+ return ;
511
+ }
512
+ final store = PerAccountStoreWidget .of (context);
513
+ store.typingNotifier.stoppedComposing ();
514
+ }
515
+
516
+ @override
517
+ void didChangeAppLifecycleState (AppLifecycleState state) {
518
+ switch (state) {
519
+ case AppLifecycleState .hidden:
520
+ case AppLifecycleState .paused:
521
+ case AppLifecycleState .detached:
522
+ // Transition to either [hidden] or [paused] signals that
523
+ // > [the] application is not currently visible to the user, and not
524
+ // > responding to user input.
525
+ //
526
+ // When transitioning to [detached], the compose box can't exist:
527
+ // > The application defaults to this state before it initializes, and
528
+ // > can be in this state (applicable on Android, iOS, and web) after
529
+ // > all views have been detached.
530
+ //
531
+ // For all these states, we can conclude that the user is not
532
+ // composing a message.
533
+ final store = PerAccountStoreWidget .of (context);
534
+ store.typingNotifier.stoppedComposing ();
535
+ case AppLifecycleState .inactive:
536
+ // > At least one view of the application is visible, but none have
537
+ // > input focus. The application is otherwise running normally.
538
+ // For example, we expect this state when the user is selecting a file
539
+ // to upload.
540
+ case AppLifecycleState .resumed:
541
+ }
542
+ }
543
+ }
544
+
543
545
/// The content input for _StreamComposeBox.
544
546
class _StreamContentInput extends StatefulWidget {
545
547
const _StreamContentInput ({required this .narrow, required this .controller});
0 commit comments