خطای "سرریز پایین صفحه" ناشی از صفحه کلید در فلاتر Bottom Overflowed

سلام! خوشحالم که دوباره با مقاله جدیدی درباره Flutter، با شما هستم. اگر تا به حال با TextFormFields در Flutter کار کرده باشید، مطمئن هستم که نوارهای زرد / سیاه روی پایین صفحه نمایش خود دیده اید، درست مانند تصویر زیر. در این مقاله ، ما در مورد بهترین راه حل برای جلوگیری از خطای سرریز پایین صفحه (Bottom Overflough) هنگام ظاهر شدن صفحه کلید صحبت خواهیم کرد.


همانطور که مشاهده می کنید ویجت ها باید به صورت عمودی نمایش داده شوند، به همین دلیل به عنوان والد به یک ویجت ستون یا همان column احتیاج داریم. رنگ پس زمینه و فاصله چپ / راست (padding) را فراموش نکنید! برای سفارشی کردن پس زمینه به یک Container با خاصیت decotation مانند کد زیر نیاز داریم. برای اعتبارسنجی قسمت فرم متن ، به یک ویجت به نام Form نیاز داریم که باید یک key برای آن تعریف کنیم.

final _formKey = new GlobalKey<FormState>(); // outside the build() method 

...@override

Widget build(BuildContext context) {
  return Scaffold(
      body: Container(
    color: Color.fromRGBO(36, 43, 47, 1),
    padding: const EdgeInsets.symmetric(horizontal: 43.0),
    child: Form(
      key: _formKey,
      child: Container(
        alignment: Alignment.center,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _buildFirstName(),
            _buildLastName(),
            _buildEmail(),
            _buildPassword(),
            _buildConfirmPassword(),
            _buildSignUpButton(context)
          ],
        ),
      ),
    ),
  ));
}

اول یک decoration برای هر یک از tetxfieldها درست کنیم

InputDecoration _buildInputDecoration(String hint, String iconPath) {
  return InputDecoration(
      focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Color.fromRGBO(252, 252, 252, 1))),
      hintText: hint,
      enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Color.fromRGBO(151, 151, 151, 1))),
      hintStyle: TextStyle(color: Color.fromRGBO(252, 252, 252, 1)),
      icon: iconPath != '' ? Image.asset(iconPath) : null,
      errorStyle: TextStyle(color: Color.fromRGBO(248, 218, 87, 1)),
      errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(color:  Color.fromRGBO(248, 218, 87, 1))),
      focusedErrorBorder: UnderlineInputBorder(
          borderSide: BorderSide(color:  Color.fromRGBO(248, 218, 87, 1))));
}


و در ادامه هر کدام از ویجت های موجد در form را جداگانه تعریف می کنیم.

_buildFirstName()

Widget _buildFirstName() {
  return TextFormField(
    validator: (value) =>
        value.isEmpty ? &quotFirst name cannot be empty&quot : null,
    style: TextStyle(
        color: Color.fromRGBO(252, 252, 252, 1), fontFamily: 'RadikalLight'),
    decoration:
        _buildInputDecoration(&quotFirst name&quot, 'assets/ic_worker.png'),
  );
}

_buildLastName()

Widget _buildLastName() {
  return Container(
      margin: const EdgeInsets.only(left: 40),
      child: TextFormField(
        validator: (value) =>
            value.isEmpty ? &quotLast name cannot be empty&quot : null,
        style: TextStyle(
            color: Color.fromRGBO(252, 252, 252, 1), fontFamily: 'RadikalLight'),
        decoration: _buildInputDecoration(&quotLast name&quot, ''),
      ));
}


_buildEmail()

Widget _buildEmail() {
  return TextFormField(
    validator: (value) => !isEmail(value) ? &quotSorry, we do not recognize this email address&quot : null,
    style: TextStyle(
        color: Color.fromRGBO(252, 252, 252, 1), fontFamily: 'RadikalLight'),
    decoration: _buildInputDecoration(&quotEmail&quot, 'assets/ic_email.png'),
  );
}

_buildPassword()

final _passwordController = TextEditingController(); //this line is outside the build() method 
...Widget _buildPassword() {
  return TextFormField(
    obscureText: true,
    controller: _passwordController,
    validator: (value) =>
        value.length <= 6 ? &quotPassword must be 6 or more characters in length&quot : null,
    style: TextStyle(
        color: Color.fromRGBO(252, 252, 252, 1), fontFamily: 'RadikalLight'),
    decoration:
        _buildInputDecoration(&quotPassword&quot, 'assets/ic_password.png'),
  );
}bool isEmail(String value) {
  String regex =
      r'^(([^<>()[\]\\.,;:\s@\&quot]+(\.[^<>()[\]\\.,;:\s@\&quot]+)*)|(\&quot.+\&quot))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

  RegExp regExp = new RegExp(regex);

  return value.isNotEmpty && regExp.hasMatch(value);
}

_buildConfirmPassword()

Widget _buildConfirmPassword() {
  return Container(
      margin: const EdgeInsets.only(left: 40),
      child: TextFormField(
        obscureText: true,
        validator: (value) => value.isEmpty ||
                (value.isNotEmpty && value != _passwordController.text)
            ? &quotMust match the previous entry&quot
            : null,
        style: TextStyle(
            color: Color.fromRGBO(252, 252, 252, 1), fontFamily: 'RadikalLight'),
        decoration: _buildInputDecoration(&quotConfirm password&quot, ''),
      ));
}

_buildSignUpButton().

Widget _buildSignUpButton(BuildContext context) {
  return Container(
    margin: const EdgeInsets.only(top: 43.0),
    width: MediaQuery.of(context).size.width * 0.62,
    child: RaisedButton(
      child: const Text(
        &quotSign Up&quot,
        style: TextStyle(
            color: Color.fromRGBO(40, 48, 52, 1),
            fontFamily: 'RadikalMedium',
            fontSize: 14),
      ),
      color: Colors.white,
      elevation: 4.0,
      onPressed: () {
        _validateAndSubmit();
      },
    ),
  );
}


خب دوستان کد رو روی صفحه کوچک تست کنید.

اگر برنامه را اجرا کنید مشکلی وجود ندارد
اگر برنامه را اجرا کنید مشکلی وجود ندارد

مشکل زمانی هست که کیبورد فعال شود. مانند شکل بالا!!!

یک راه حل سریع این است که ویجت های داخل Scaffold را مسدود کنید تا در زمان باز شدن صفحه کلید تغییر اندازه دهند اما از این طریق برخی ویجت ها توسط صفحه کلید مبهم می شوند. ما می توانیم این کار را با استفاده از ویژگی resizeToAvoidBottomInset در ویجت Scaffold انجام دهیم. روش ساخت به شرح زیر است:

@override
Widget build(BuildContext context) {
  return Scaffold(
      resizeToAvoidBottomInset: false,   //new line
      body: Container(
        color: Color.fromRGBO(36, 43, 47, 1),
        padding: const EdgeInsets.symmetric(horizontal: 43.0),
        child: Form(
          key: _formKey,
          child: Container(
            alignment: Alignment.center,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                _buildFirstName(),
                _buildLastName(),
                _buildEmail(),
                _buildPassword(),
                _buildConfirmPassword(),
                _buildSignUpButton(context)
              ],
            ),
          ),
        ),
      ));
}


راه حل دیگر این است که ویجت Column را در یک ویجت قابل پیمایش قرار دهید. ویجت قابل پیمایش ساخته شده توسط Flutter که به خوبی کار می کند SingleChildScrollView است. این بهترین راه حل برای جلوگیری از خطای "Bottom overflough" هنگام باز شدن صفحه کلید است.

@override
Widget build(BuildContext context) {
  return Scaffold(
      body: Container(
    color: Color.fromRGBO(36, 43, 47, 1),
    padding: const EdgeInsets.symmetric(horizontal: 43.0),
    child: Form(
      key: _formKey,
      child: Container(
        alignment: Alignment.center,
        child: SingleChildScrollView(    // new line
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              _buildFirstName(),
              _buildLastName(),
              _buildEmail(),
              _buildPassword(),
              _buildConfirmPassword(),
              _buildSignUpButton(context)
            ],
          ),
        ),
      ),
    ),
  ));
}

امیدوارم که این مقاله به شما کمک کند که درک بهتری داشته باشین که چرا صفحه کلید باعث ایجاد خطای "Bottom overflough" می شود ، و همچنین چگونه آن را برطرف کنید.

با ما همراه باشید ، مقالات بیشتری درباره Flutter در راه است!

ممنون از خواندن!