'how can I make scrollable and responsive screen flutter

I have implemented a login screen using flutter. the screen is fully functional and working properly with validations. I want this screen to be scrollable and responsive to other devices. how can I do that? below I have added my full login screen code. appreciate your help on this.

  1. how to scroll the screen
  2. how to implement responsive screen

class LoginScreen extends StatefulWidget { const LoginScreen({Key? key}) : super(key: key);

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

class _LoginScreenState extends State<LoginScreen> {
  @override
  Widget build(BuildContext context) {
    final Size = MediaQuery.of(context).size;
    return GestureDetector(
      onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
      child: Container(
        decoration: const BoxDecoration(
            gradient: LinearGradient(
              colors: [Color.fromARGB(255, 3, 86, 124), Color(0xff141a3a)],
              begin: Alignment.topRight,
              end: Alignment.bottomLeft,
            )),
        child: Scaffold(
          backgroundColor: Colors.transparent,
          resizeToAvoidBottomInset: false,
          body: Padding(
            padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
            child: Column(
              children: [
                Expanded(
                  flex: 2,
                  child: Column(
                    children: [
                      //  Spacer(flex: 1),
                      Image(
                        image: const AssetImage(
                          'assets/images/LogoVector.png',
                        ),
                        height: Size.width / 2.9,
                        width: Size.width / 2.9,
                      ),
                      SizedBox(height: 5),
                      Text(
                        "LOGIN",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontSize: 30,
                            color: textWhite,
                            fontFamily: "Roboto"),
                      ),
                    ],
                  ),
                ),
                // const Spacer(flex: 1),
                const Expanded(flex: 3, child: Center(child: LoginForm())),
                // Spacer(
                //   flex: 1,
                // ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class LoginForm extends StatefulWidget {
  const LoginForm({Key? key}) : super(key: key);

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

Map<String, String> loginUserData = {
  'email': '',
  'password': '',
  'id': '',
  'userName': '',
  'token': '',
  'userStatus': '',
};

class _LoginFormState extends State<LoginForm> {
  TextEditingController emailEditingController = new TextEditingController();
  TextEditingController passwordEditingController = new TextEditingController();
  final _formKey = GlobalKey<FormState>();
  String email = "";
  String password = "";
  String username = "";
  bool isLoading = false;
  bool typing = true;
  bool _isObscure = true;

  // bool newValue = true;
  bool checkedValue = true;

  //String fcmToken = '';

  Future LoginData() async {
    setState(() {
      isLoading = true;
      typing = false;
    });
    try {
      var response = await Dio().post(BASE_API + 'user/login',
          data: {"username": email, "password": password});

      if (response.data["status"] == "LoginSuccess") {
        setState(() {
          isLoading = false;
        });

        Get.snackbar(
          "success",
          "logged in successfully",
          backgroundColor: buttontext.withOpacity(0.5),
          colorText: textWhite,
          borderWidth: 1,
          borderColor: Colors.grey,
        );

         Get.to(BottomNavigation());
      } else {
        setState(() {
          isLoading = false;
          typing = true;
        });
        Get.snackbar(
          "error",
          "No User Found",

          backgroundColor: buttontext.withOpacity(0.5),
          colorText: textWhite,
          borderWidth: 1,
          borderColor: Colors.grey,
        );
      }
      print("res: $response");
      setState(() {
        isLoading = false;
        typing = true;
      });
    } catch (e) {
      setState(() {
        isLoading = false;
        typing = true;
      });
      Get.snackbar("Error", "Something went wrong.Please contact admin",
          backgroundColor: buttontext.withOpacity(0.5),
          borderWidth: 1,
          borderColor: Colors.grey,
          colorText: Colors.white,
          icon: Icon(
            Icons.error_outline_outlined,
            color: Colors.red,
            size: 30,
          ));
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    final Size = MediaQuery.of(context).size;
    return Form(
      key: _formKey,
      autovalidateMode: AutovalidateMode.disabled,
      child: Column(
        children: [
          TextFormField(
            controller: emailEditingController,
            enabled: true,
            decoration: InputDecoration(
              enabledBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(30.0),
                borderSide: const BorderSide(
                  color: textWhite,
                ),
                // borderSide: BorderSide.none
              ),
              focusedBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(30.0),
                borderSide: const BorderSide(color: textWhite),
              ),

              errorBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(30.0),
                borderSide: const BorderSide(color: Colors.red),
              ),
              focusedErrorBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(30.0),
                borderSide: const BorderSide(color: Colors.red),
              ),
              isDense: true,
              contentPadding: EdgeInsets.fromLTRB(10, 30, 10, 0),

              hintText: "Email/ Username",
              hintStyle: TextStyle(
                  color: textWhite, fontFamily: "Roboto", fontSize: 14),
            ),
            style: TextStyle(color: textWhite),
            validator: (String? UserName) {
              if (UserName != null && UserName.isEmpty) {
                return "Email can't be empty";
              }
              return null;
            },
            onChanged: (String? text) {
              email = text!;
              // print(email);
            },
            onSaved: (value) {
              loginUserData['email'] = value!;
            },
          ),
          SizedBox(
            height: 10,
          ),
          TextFormField(
            controller: passwordEditingController,
            obscureText: _isObscure,
            enabled: true,
            decoration: InputDecoration(
                enabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(30.0),
                  borderSide: const BorderSide(color: textWhite),
                  //  borderSide: BorderSide.none
                ),
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(30.0),
                  borderSide: const BorderSide(color: textWhite),
                ),
                errorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(30),
                  borderSide: const BorderSide(color: Colors.red),
                ),
                focusedErrorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(30),
                  borderSide: const BorderSide(color: Colors.red),
                ),
                isDense: true,
                contentPadding: EdgeInsets.fromLTRB(10, 10, 10, 0),
                suffixIcon: IconButton(
                    icon: Icon(
                        _isObscure ? Icons.visibility : Icons.visibility_off),
                    color: textWhite,
                    onPressed: () {
                      setState(() {
                        _isObscure = !_isObscure;
                      });
                    }),
                hintText: "Password",
                hintStyle: TextStyle(
                  color: textWhite,
                  fontFamily: "Roboto",
                  fontSize: 14,
                )),
            style: TextStyle(color: textWhite),
            validator: (String? Password) {
              if (Password != null && Password.isEmpty) {
                return "Password can't be empty";
              }
              return null;
            },
            onChanged: (String? text) {
              password = text!;
              print(password);
            },
            onSaved: (value) {
              loginUserData['password'] = value!;
            },
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Expanded(
                child: CheckboxListTile(
                  title: const Text(
                    "Remember Me",
                    style: TextStyle(
                        color: textWhite, fontFamily: "Roboto", fontSize: 14),
                  ),
                  activeColor: buttontext,
                  // tileColor: buttontext,

                  value: checkedValue,
                  onChanged: (newValue) {
                    FocusManager.instance.primaryFocus?.unfocus();
                    setState(() {
                      if (isLoading != true) {
                        checkedValue = newValue!;
                        print(newValue);
                      }
                    });
                  },
                  contentPadding: EdgeInsets.only(left: 0, top: 0),
                  controlAffinity:
                  ListTileControlAffinity.leading, //  <-- leading Checkbox
                ),
              ),
              TextButton(
                child: Text(
                  "Forget Password",
                  style: TextStyle(
                      color: textWhite, fontFamily: "Roboto", fontSize: 14),
                ),
                onPressed: () {
                  Get.to(() => Forget_Screen());
                },
              )
            ],
          ),
          SizedBox(height: 40),
          isLoading
              ? SpinKitDualRing(
            color: textWhite,
            size: 40,
          )
              : GestureDetector(
            child: MainButton("Login"),
            onTap: () async {
              FocusManager.instance.primaryFocus?.unfocus();
              if (_formKey.currentState!.validate()) {
                _formKey.currentState!.save();
                await LoginData();
                // Get.to(BottomNavigation());
              }
            },
          ),
          SizedBox(height: 15),
          Container(
            width: 275.0,
            height: 40.0,
            child: OutlinedButton(
              onPressed: () {
                Get.to(() => const Signup_Screen());
              },
              child: const Text(
                'Signup',
                style: TextStyle(
                    fontSize: 15, fontFamily: "Roboto", color: textWhite
                  //fontWeight: FontWeight.w500,
                  // color: Colors.black,
                ),
              ),
              style: OutlinedButton.styleFrom(
                side: const BorderSide(
                  width: 1.0,
                  color: textWhite,
                ),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(18.0),
                  // side: BorderSide(width: 2, color: Colors.green),
                ),
              ),
            ),
          )
        ],
      ),
    );
  }
}


Solution 1:[1]

How to scroll the screen? Use SingleChildScrollView wrap your Column, remember to ristrict the content size to avoid error.

How to implement responsive screen? Simplest is use LayoutBuilder widget that provide constrains as a props. This constrains can provide maxHeight, maxWidth,... You can then rely on it to determine how you build your application (responsive).

Example:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: LayoutBuilder(
      builder: (context, constrains){
        final maxw = constrains.maxWidth;
        String text = '';
        if(maxw < 420) {text = 'Smaller than 420';}
        else {text = 'Bigger than 420';}
        return Center(child: Text('Width is: $maxw, $text'));
      }
    ),
  );
}

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1