Jane
Jane

Reputation: 1

With Flutter, how to make a scrollable screen contain a widget that can expand or not depending on whether the soft keyboard is opened or closed?

I am a Flutter beginner and I am currently trying to implement a login screen which must satisfy to the following requirements:

Here are wireframes that describe what I would like to achieve with Flutter:

state: soft keyboard closed
state: soft keyboard closed

state: soft keyboard opened
state: soft keyboard opened

Is this feasible with Flutter? Currently here is what I have attempted:

import 'package:flutter/material.dart';

import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';


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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isSoftKeyboardOpen;

  @override
  void initState() {
    super.initState();

    var keyboardVisibilityController = KeyboardVisibilityController();

    _isSoftKeyboardOpen = keyboardVisibilityController.isVisible;

    // Subscribe
    keyboardVisibilityController.onChange.listen((bool visible) {
      setState(() {
        _isSoftKeyboardOpen = visible;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    var mAppBar = AppBar(
      // Here we take the value from the MyHomePage object that was created by
      // the App.build method, and use it to set our appbar title.
      title: Text(widget.title),
    );

    return Scaffold(
      appBar: mAppBar,
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: LayoutBuilder(
          builder: (context, constraint) {
            return SingleChildScrollView(
                padding: EdgeInsets.only(left: 16, right: 16),
                child: ConstrainedBox(
                  constraints: BoxConstraints(minHeight: constraint.maxHeight),
                  child: LayoutBuilder(
                      builder: (containerContext, constraint) {
                        return Container(
                          height: MediaQuery.of(containerContext).size.height - mAppBar.preferredSize.height - MediaQuery.of(context).padding.top,
                          color: Colors.green,
                          child: Column(
                            children: <Widget> [
                              Expanded(
                                flex: _isSoftKeyboardOpen ? 0 : 1,
                                child: Column(
                                  children: <Widget> [
                                    TextFormField(
                                      decoration: InputDecoration(
                                        labelText: "Username",
                                      ),
                                    ),
                                    TextFormField(
                                      decoration: InputDecoration(
                                        labelText: "Password",
                                      ),
                                    )
                                  ],
                                ),
                                ),
                                ElevatedButton(onPressed: null, child: Text("Sign in")
                              ),
                            ],
                          ),
                        );
                      }
                  ),
                )
            );
          },
        )
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

As you can see when the soft keyboard opens, the screen scrolls but there is unnecessary space below the button (which is the last element of the screen). Is there a way for me to change the screen height dynamically in my code to achieve what I want? Or is there another way to implement the sign in screen which fulfills my requirements.

Upvotes: 0

Views: 612

Answers (2)

Kapil Sahu
Kapil Sahu

Reputation: 599

Hi @Jane you can achieve the desired output using the below code.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LoginPage(),
    );
  }
}

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('SIGN IN'),
        ),
        body: LayoutBuilder(builder: (context, constraint) {
          return ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.all(16),
            children: [
              GestureDetector(
                onTap: () {
                  FocusScope.of(context).unfocus();
                },
                child: Container(
                  color: Colors.white,
                  height: constraint.maxHeight -
                      32, // -32 to remove vertical padding
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Column(
                        children: [
                          TextFormField(
                            decoration: InputDecoration(
                              labelText: "Username",
                            ),
                          ),
                          TextFormField(
                            decoration: InputDecoration(
                              labelText: "Password",
                            ),
                          ),
                        ],
                      ),
                      ElevatedButton(onPressed: null, child: Text("Sign in"))
                    ],
                  ),
                ),
              ),
            ],
          );
        }));
  }
}

Also, try to use plugins only in dire situations when you can't achieve a particular task with flutter available resources.

Upvotes: 0

Gourango Sutradhar
Gourango Sutradhar

Reputation: 1619

You can user the below widget to gain your requirements:

return KeyboardVisibilityBuilder(
  builder: (context, child, isKeyboardVisible) {
    if (isKeyboardVisible) {
      // build layout for visible keyboard
    } else {
      // build layout for invisible keyboard
    }
  },
  child: child, // this widget goes to the builder's child property. Made for better performance.
);

Upvotes: 1

Related Questions