Altherat
Altherat

Reputation: 701

Detect when widget changes position in Flutter

Is there any way to detect when a widget changes position? Such as when the keyboard pops up and the content is shifted up? I would like to detect this without relying on focus events or trying to detect the keyboard state.

Here's an example app:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyTextField()
        )
      )
    );
  }
}

class MyTextField extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return TextField();
  }

}

How can you detect when MyTextField is moved up when it is focused?

Upvotes: 3

Views: 5338

Answers (2)

Till A. S. Friebe
Till A. S. Friebe

Reputation: 1439

If you just want to find out if the keyboard is visible, you can use this:

import 'dart:ui';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    print('Is the keyboard visible? ${window.viewInsets.bottom != 0 ? 'yes' : 'no'}');
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: TextField(),
        ),
      ),
    );
  }
}

Explaination: By getting the bottom position of the rectangle in which your app is drawn, you can deduce whether a system component has reduced the space of your app, hence if the keyboard is visible.

This answers only this part of your question:

Such as when the keyboard pops up and the content is shifted up?

But at least the size of the rectangle in which your app is drawn is always the current one and not an old size. This is the problem with @diegoveloper's answer, you always get the old position of the TextField.

Upvotes: 0

diegoveloper
diegoveloper

Reputation: 103401

You can use WidgetsBindingObserver to detect when the metrics change, here you have a sample but you will have to use GlobalKey to check the new position of your Widget :

    class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
      GlobalKey _key = GlobalKey();

      @override
      void initState() {
        WidgetsBinding.instance.addObserver(this);
        super.initState();
      }

      @override
      void dispose() {
        WidgetsBinding.instance.removeObserver(this);
        super.dispose();
      }

      @override
      void didChangeMetrics() {
        final RenderBox renderBox = _key.currentContext.findRenderObject();
        final position = renderBox.localToGlobal(Offset.zero);
        print("position : ${position.dx},${position.dy}");
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: MyTextField(
              key: _key,
            ),
          ),
        );
      }
    }

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

      @override
      Widget build(BuildContext context) {
        return TextField();
      }
    }

Upvotes: 7

Related Questions