이번에는 유튜브 모바일과 데스크톱의 레이아웃이 다른 것처럼 반응형으로 웹앱의 Layout을 만드는 강의였다.

 

 

main.dart

import 'package:flutter/material.dart';
import 'my_page.dart';
void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,

      ),
      home: MyPage(),
    );
  }
}

 

my_page.dart

- body를 ResponsiveLayout 위젯을 만들어 반응형으로 화면 구성

- ResponsiveLayout은 생성자에서 2개의 변수를 초기화 해야하므로 mobileBody, desktopBody를 매개변수로 넘겨줌

import 'package:flutter/material.dart';
import 'package:mild_2_youtube/responsive/desktop_body.dart';
import 'package:mild_2_youtube/responsive/mobile_body.dart';
import 'package:mild_2_youtube/responsive/responsive.dart';

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

  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: ResponsiveLayout(
        mobileBody: MobileBody(),
        desktopBody: DesktopBody(),
      ),
    );
  }
}

 

 

responsive.dart

- LayoutBuilder클래스를 사용해서 화면너비에 따라 모바일, 데스크탑용으로 화면layout을 바꿔주기 위해 사용

참고: https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html

- 제약조건을 거는데 최대 너비가 800이하일 경우 모바일, 이상일 경우 데스크탑 위젯 화면 보여줌

import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  const ResponsiveLayout(
      {Key? key, required this.mobileBody, required this.desktopBody})
      : super(key: key);

  final Widget mobileBody;
  final Widget desktopBody;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      if (constraints.maxWidth < 800) {
        return mobileBody;
      } else {
        return desktopBody;
      }
    }); //기기 전체가 아니라 위젯의 크기를 알아내기
  }
}

 

mobile_body.dart

- 모바일 화면구성

- 컬럼 내에서 listview를 사용할 때 expanded를 감싸서 공간의 범위를 제한해줌

import 'package:flutter/material.dart';


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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Mobile'),
      ),
      backgroundColor: Colors.deepPurple,
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Container(
              height: 300,
              color: Colors.deepPurple[400],
            ),
          ),
          Expanded( //컬럼 내에서 Listview사용할 때는 Expanded로 감싸줘야함, 안그러면 container의 height를 쓸 수 없음
            child: ListView.builder(
                itemCount: 10,
                itemBuilder: (context, index){
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      color: Colors.deepPurple[300],
                      height: 120,
                    ),
                  );
                }),
          )
        ],
      ),
    );
  }
}

 

 

desktop_body.dart

- sidePanel의 너비를 300으로 고정하고 나머지는 너비를 주지않았고 리스트뷰를 expanded로 감쌈으로 해서 화면 크기 변화에 따라 1열의 너비만 변하는 것을 볼 수 있다.

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Desktop'),
      ),
      backgroundColor: Colors.deepPurple[200],
      body: Row(
        children: [
          Expanded(
            child: Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Container(
                    height: 300,
                    color: Colors.deepPurple[400],
                  ),
                ),
                Expanded( //컬럼 내에서 Listview사용할 때는 Expanded로 감싸줘야함, 안그러면 container의 height를 쓸 수 없음
                  child: ListView.builder(
                      itemCount: 10,
                      itemBuilder: (context, index){
                        return Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Container(
                            color: Colors.deepPurple[300],
                            height: 120,
                          ),
                        );
                      }),
                ),
              ],
            ),
          ),
          //side Panel
          Container(
            width: 300,
            child: ListView.builder(
                itemCount: 10,
                itemBuilder: (context, index){
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      color: Colors.deepPurple[300],
                      height: 120,
                    ),
                  );
                }),
          )
        ],
      ),
    );
  }
}

 

 

 

 

요약 - 리스트 아이템을 선택하면 동물페이지가 뜨고 좋아요를 누를 수 있게 됨

주요 기능

 

 

main.dart

- 메인 페이지이며 리스트 제너레이터 사용법 확인

- navigator를 통해 animal_page로 넘어갈 때 animalData를 넘겨줌

import 'package:flutter/material.dart';
import 'package:mild_2_listview/animal_page.dart';
import 'package:mild_2_listview/model.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyPage(),
    );
  }
}

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

  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  static List<String> animalName = [
    'Bear',
    'Camel',
    'Deer',
    'Fox',
    'Kangaroo',
    'Koala',
    'Lion',
    'Tiger'
  ];

  static List<String> animalImagePath = [
    'image/bear.png',
    'image/camel.png',
    'image/deer.png',
    'image/fox.png',
    'image/kangaroo.png',
    'image/koala.png',
    'image/lion.png',
    'image/tiger.png'
  ];

  static List<String> animalLocation = [
    'forest and mountain',
    'dessert',
    'forest',
    'snow mountain',
    'Australia',
    'Australia',
    'Africa',
    'Korea'
  ];

  final List<Animal> animalData = List.generate(
      animalLocation.length,
      (index) => Animal(
          animalName[index], animalImagePath[index], animalLocation[index]));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("ListView"),
        ),
        body: ListView.builder(
            itemCount: animalData.length,
            itemBuilder: (context, index) {
              //debugPrint(index.toString()+" : "+animalData[index].imgPath.toString());
              return Card(
                  child: ListTile(
                      title: Text(animalData[index].name),
                      leading: SizedBox(
                        height: 50,
                        width: 50,
                        child: Image.asset(animalData[index].imgPath),
                      ),
                    onTap: (){
                        Navigator.of(context).push(MaterialPageRoute(
                            builder: (context)=>AnimalPage(animal: animalData[index],)));
                        debugPrint(animalData[index].name);
                    },
                  ));
            }));
  }
}

 

animal_page.dart

- 넘겨받음 animal데이터로 page구현

- like_button패키기 사용(https://pub.dev/packages/like_button/install)

import 'package:flutter/material.dart';
import 'package:mild_2_listview/model.dart';
import 'package:like_button/like_button.dart';

class AnimalPage extends StatefulWidget {
  const AnimalPage({Key? key, required this.animal}) : super(key: key);
  //null 값을 가질 수 없는 this.animal에는 required를 붙여준다
  final Animal animal;

  @override
  State<AnimalPage> createState() => _AnimalPageState();
}

class _AnimalPageState extends State<AnimalPage> {
  bool isLiked = false;
  int likeCount = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.animal.name),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
                height: 200, width: 200, child: Image.asset(widget.animal.imgPath)),
            const SizedBox(
              height: 20,
            ),
            Text('It lives in ' + widget.animal.location,
            style: const TextStyle(
              fontSize: 18
            ),),
            const SizedBox(
              height: 20,
            ),
            LikeButton(
              size: 30,
                isLiked: isLiked,
                likeCount: likeCount,
            )
          ],
        ),
      ),
    );
  }
}

 

model.dart

- Animal클래스

import 'package:flutter/material.dart';

class Animal{
  final String name;  //final 은 runtime시에 값이 정해질 수 있기 때문에
  final String imgPath;
  final String location;

  Animal(this.name, this.imgPath, this.location);
}

 

 

 

1. Alt + Enter - 라이브러리 추가 등

2. Shift + Enter - 다음칸으로 커서 이동

 

 

1. 리스트뷰 vs 리스트뷰빌더

  - 리스트뷰와 리스트뷰빌더가 있는데 리스트뷰는 모든 리스트를 한 번에 불러오기 때문에 로딩이슈가 발생 할 수 있다.

  - 리스트뷰빌더는 필요한 것만 그때 그때 불러서 사용

2. 리스트뷰빌더예제

  - 리스트뷰형식에 터치를 하면 상세화면이 나옴

  - 주요기능은

    1) 리스트뷰 구현 - 리스트뷰빌더사용하였고 터치를 위해 GestureDetector사용

    2) 팝업창(상세화면) 구현 - showpopup이라는 함수를 만들어서 구현

    3)가로 세로윈도우가 바뀌었을 때도 반응형으로 동작 - description을 구현할 때 mediaquery 클래스 사용

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: ListViewPage(),
    );
  }
}

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

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var titleList = [
    'Dentist',
    'Pharmacist',
    'School teacher',
    'IT manager',
    'Account',
    'Lawyer',
    'Hairdresser',
    'Physician',
    'Web developer',
    'Medical Secretary'
  ];

  var imageList = [
    'image/1.png',
    'image/2.png',
    'image/3.png',
    'image/4.png',
    'image/5.png',
    'image/6.png',
    'image/7.png',
    'image/8.png',
    'image/9.png',
    'image/10.png'
  ];

  var description = [
    '1.There are different types of careers you can pursue in your life. Which one will it be?',
    '2.There are different types of careers you can pursue in your life. Which one will it be?',
    '3.There are different types of careers you can pursue in your life. Which one will it be?',
    '4.There are different types of careers you can pursue in your life. Which one will it be?',
    '5.There are different types of careers you can pursue in your life. Which one will it be?',
    '6.There are different types of careers you can pursue in your life. Which one will it be?',
    '7.There are different types of careers you can pursue in your life. Which one will it be?',
    '8.There are different types of careers you can pursue in your life. Which one will it be?',
    '9.There are different types of careers you can pursue in your life. Which one will it be?',
    '10.There are different types of careers you can pursue in your life. Which one will it be?'
  ];

  void showPopup(context, title, image, description) {
    showDialog(
        context: context,
        builder: (context) {
          return Dialog(
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              height: 380,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10), color: Colors.white),
              child: Column(
                children: [
                  ClipRRect(
                      borderRadius: BorderRadius.circular(10),
                      child: Image.asset(
                        image,
                        width: 200,
                        height: 200,
                      )),
                  const SizedBox(
                    height: 10,
                  ),
                  Text(
                    title,
                    style: const TextStyle(
                      fontSize: 25,
                      fontWeight: FontWeight.bold,
                      color: Colors.grey,
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Text(
                      description,
                      maxLines: 3, //최대 3줄까지 표현
                      style: TextStyle(fontSize: 15, color: Colors.grey[500]),
                      textAlign: TextAlign.center,
                    ),
                  ),
                  ElevatedButton.icon(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      icon: const Icon(Icons.close),
                      label: const Text('close')),
                ],
              ),
            ),
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    double width =
        MediaQuery.of(context).size.width * 0.6; //반응형을 위해 디스크립션을 60%크기로
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'ListView',
          style: TextStyle(color: Colors.grey),
        ),
        backgroundColor: Colors.white,
        elevation: 0,
      ),
      body: ListView.builder(
        itemCount: titleList.length,
        itemBuilder: (context, index) {
          return GestureDetector(  //InkWell 도 사용가능
            onTap: () {
              debugPrint(titleList[index]);
              showPopup(context, titleList[index], imageList[index],
                  description[index]);
            },
            child: Card(
                child: Row(children: [
              SizedBox(
                width: 100,
                height: 100,
                child: Image.asset(imageList[index]),
              ),
              Padding(
                  padding: const EdgeInsets.all(10),
                  child: Column(
                    children: [
                      Text(titleList[index],
                          style: const TextStyle(
                              fontSize: 22,
                              fontWeight: FontWeight.bold,
                              color: Colors.grey)),
                      const SizedBox(
                        height: 10,
                      ),
                      SizedBox(
                          width: width,
                          child: Text(description[index],
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.grey[500],
                              )))
                    ],
                  ))
            ])),
          );
        },
      ),
    );
  }
}

 

 

온보딩 스크린은 앱을 처음 설치했을 때 간단한 안내사항 및 튜토리얼 같은 것을 안내하는 페이지

 

1. 구현을 위해 다음 패키지를 설치해야한다

introduction_screen: ^3.1.2

https://pub.dev/packages/introduction_screen

 

introduction_screen | Flutter Package

Introduction/Onboarding package for flutter app with some customizations possibilities

pub.dev

 

2. 파일은

main.dart (기본 화면) - 앱의 메인화면이 시작 될 것

onboarding.dart (온보딩 스크린 꾸미기)

 

3. main.dart

Elevated버튼 부분에서 push 대신 pushReplacement를 사용했는데 뒤로가기 버튼을 없앨 수 있다.

MaterialApp의 홈부분에서 MyPage로 가는게 아니라 OnBoardingPage로 연결되는 것이 포인트

(사실 온보딩페이지는 1번만 보여주고 그 다음부터는 보여주면 안되는 것이지만 우리는 연습을 위해서 이렇게 함)

import 'package:flutter/material.dart';
import 'onboarding.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: OnBoardingPage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Main page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Main Screen',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
            ),
            ElevatedButton(
                onPressed: () {
                  Navigator.of(context).pushReplacement(
                      MaterialPageRoute(builder: (context) => const OnBoardingPage()));
                },
                child: const Text('Go to onboarding Screen'))
          ],
        ),
      ),
    );
  }
}

 

4. onboarding.dart

우선 IntroductionScreen에 보면 아주 다양한 속성들을 가지고 있는데 여기서 우리는 아래 속성들을 편집할 예정

page: 배치할 페이지 꾸미는 부분

done: 마지막 화면에 띄워줄 글씨

onDone: 마지막 화면에서 메인 페이지로 연결

next: 다음 화면 표시할 것 (우리는 forward 아이콘 사용)

showSkipButton (skip버튼 사용 여부)

skip: 화면에 표시할 것

dotsDecoration: 화면 진행상황 표시 점을 꾸미기

curve: 에니메이션 효과 넣기

 

import 'package:flutter/material.dart';
import 'package:introduction_screen/introduction_screen.dart';

import 'main.dart';

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

  @override
  Widget build(BuildContext context) {
    return IntroductionScreen(
      pages: [
        PageViewModel(
            title: 'Welcome to my app',
            body: 'This is the first Page',
            image: Image.asset('image/page1.png'),
            decoration: getPageDecoration()),
        PageViewModel(
            title: 'Welcome to my app',
            body: 'This is the second Page',
            image: Image.asset('image/page2.png'),
            decoration: getPageDecoration()),
        PageViewModel(
            title: 'Welcome to my app',
            body: 'This is the third Page',
            image: Image.asset('image/page3.png'),
            decoration: getPageDecoration()),
      ],
      done: const Text('Done!'),
      onDone: () {
        Navigator.of(context)
            .pushReplacement(MaterialPageRoute(builder: (context) => const MyPage()));
      },
      next: const Icon(Icons.arrow_forward),
      showSkipButton: true,
      skip: const Text("Skip"),
      dotsDecorator: DotsDecorator(
          color: Colors.cyan,
          size: const Size(10, 10),
          activeSize: const Size(22, 10),
          activeShape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(24))),
      curve: Curves.bounceOut,
    );
  }
}

PageDecoration getPageDecoration() {
  return const PageDecoration(
    titleTextStyle: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
    bodyTextStyle: TextStyle(
      fontSize: 18,
      color: Colors.blue,
    ),
    imagePadding: EdgeInsets.only(top: 40),
    pageColor: Colors.orange,
  );
}

 

페이지별로 동일한 데코레이션을 모든 페이지마다 따로 코드를 작성한다면 너무 비효율적임

그래서 우리는 pageDecoration메서드를 만들어서 decoration을 함수 호출방식으로 사용

반복작업을 효과적으로 바꿈

route는 하나의 페이지라고 생각하면 된다.

이번에는 다른 파일의 페이지를 여는 방식을 살펴볼 것이다. Navigator1에서 다룬 내용은 싱글 페이지에서 페이지 연결하는 방식? 이라 생각됨(아직은 잘 몰라서 ..)

MaterialApp에서 home을 사용하는게 아니라 initialRoute와 routes를 이용해서 페이지 구조를 연결함

Navigator의 pushNamed를 사용

즉, home과는 다른 방식

4개의 파일 구조는 아래와 같다.

 

main. dart

import 'package:flutter/material.dart';
import 'package:flutter_1229/ScreenA.dart';
import 'package:flutter_1229/ScreenB.dart';
import 'package:flutter_1229/ScreenC.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute:'/',
      routes: {
        '/' : (context) => ScreenA(),
        '/b' : (context) => ScreenB(),
        '/c' : (context) => ScreenC(),
      },
    );
  }
}

 

 

ScreenA.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('ScreenA')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                onPressed: () {
                  Navigator.pushNamed(context, '/b');
                },
                child: Text('Go to ScreenB')),
            ElevatedButton(
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                onPressed: () {
                  Navigator.pushNamed(context, '/c');
                },
                child: Text('Go to ScreenC'))
          ],
        ),
      ),
    );
  }
}

 

ScreenB.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ScreenB"),
        ),
        body: Center(
          child: Text(
            'ScreenB',
            style: TextStyle(fontSize: 24),
          ),
        ));
  }
}

 

ScreenC.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ScreenC"),
        ),
        body: Center(
          child: Text(
            'ScreenC',
            style: TextStyle(fontSize: 24),
          ),
        ));
  }
}

 

 

 

 

 

 

* 우선 22~23강의 route개념은 중요하므로 여러번 봐야할 듯 , 그리고 24강 끝에 해답이있는데 이것이 핵심? ㅜㅜ

1. Route는 페이지라 생각 하면 됨

2. Navigator는 push, pop을 사용하며 stack형태를 이룸

3. push할 때 MaterialPageRoute(builder)를 사용함 *

  - BuildContext정보가 중요함

** 매우 중요한 것 MaterialPageRoute에서 context 인자로 넘겨줄 때 MaterialApp의 상위 위젯의 context를 넘겨주면 안된다. 그렇기에 아래 push처럼 MaterialApp의 하위 child의 context를 넘겨줘야함.

(아래 코드들에서 첫 route는 First이고 두번째 route는 Second임)

push 핵심 코드

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('First'),
      centerTitle: true,
    ),
    body: Center(
      child: ElevatedButton(
        child: Text('Go to the Second Page'),
      onPressed: () {
        Navigator.push(context,
          MaterialPageRoute(builder: (BuildContext context) {
          return Second();
        }));
      },
    )),
  );
}

만약 builder의 매개변수를 사용하지 않을 경우 아래처럼 _ 를 사용하는 것을 추천함

onPressed: () {
  Navigator.push(context,
    MaterialPageRoute(builder: (_) => Second()
  ));
},

 

pop 핵심 코드

Widget build(BuildContext context2) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Second'),
      centerTitle: true,
    ),
    body: Center(
        child: ElevatedButton(
      child: Text('Go to the First Page'),
      onPressed: () {
        Navigator.pop(context2);
      },
    )),
  );
}

 

 

전체 코드

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Toast Message',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: First(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
        child: Text('Go to the Second Page'),
        onPressed: () {
          Navigator.push(context,
            MaterialPageRoute(builder: (BuildContext context) {
            return Second();
          }));
        },
      )),
    );
  }
}

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

  @override
  Widget build(BuildContext context2) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
        child: Text('Go to the First Page'),
        onPressed: () {
          Navigator.pop(context2);
        },
      )),
    );
  }
}

 

추천 사이트: Flutter Layout Cheat Sheet. Do you need simple layout samples for… | by Tomek Polański | Flutter Community | Medium

1. Column

- 아래처럼 새로로 배치할 때 사용하며, 중요한 건 컬럼은 아래 공간까지 다 차지한다는 것!

- 정렬방법1. Center위젯으로 감싸면 아래처럼 되고

- 정렬방법2. Column에 있는 위젯들을 세로로 정렬할 때는 아래와 같은 속성을 사용할 수 있다.

mainAxisAlignment: MainAxisAlignment.spaceEvenly

- 정렬방법3. 조금 중요한데 요소들을 오른쪽 끝에 붙이고 싶으면?

crossAxisAlignment: CrossAxisAlignment.end,

위와 같이 명령어를 써도 실제로는 동작하지 않는다.. 왜그럴까? 일단 동작하는 건 맞다. 하지만 가로 폭이 요소폭이랑 같아서 변화가 없는 것

아래와 같은 invinsible Container를 사용하면 된다.

Container(
  width: double.infinity,
)

 

 

 

 

 

 

 

 

 

사용 결과

 

- 정렬방법4. 컨테이너를 가로로 꽉 채우고 싶으면? 아래와 같이 주고 각 컨테이너의 폭width를 다 없애준다.

crossAxisAlignment: CrossAxisAlignment.stretch,

- 정렬방법5. 박스간의 간격을 조정하고 싶을 때는 SizedBox를 넣어서 간격 조절하기

 

 

2. Row는 Column과 반대 개념

+ Recent posts