// URI를 나타내는 클래스 속성. // Dog클래스에서 렌더링 된다. String renderUrl;
Widget get dogImage { return Container( // 명시적으로 Container의 width, height를 설정할 수 있다. // 그렇지 않으면 children의 공간만큼 차지한다. width: 100.0, height: 100.0, // Decoration 속성은 컨테이너를 설정할 수 있게 해준다 // 이것은 BoxDecoration을 기대한다. decoration: BoxDecoration( // BoxDecorations have many possible properties. // BoxDecorations은 여러가지 가능한 속성을 가지고 있다. // BoxShape를 배경 이미지와 함께 사용하면 아바타 스타일로 잘라진 원circle)을 쉽게 만들 수 있다. shape: BoxShape.circle, image: DecorationImage( // CSS의 imagesize와 동일한 속성. fit: BoxFit.cover, // NetworkImage위젯은 URL로 이미지를 가져 오는 위젯이다. // ImageProviders (예) NetworkImage)는 이미지를 로드하거나 변경할 필요가 있을때 이상적이다. // 오류를 방지하려면 Null체크를 사용하라. image: NetworkImage(renderUrl ?? ''), ), ), ); }
이미지를 보기 위해선 Dog클래스에 인터넷을 통해 이미지를 가져오게 해야 한다. dog_cart.dart안의 _DogCardState클래스에 다음 코드를 추가하자.
// State 클래스들은 state가 생성될때 이 메서드를 실행한다. // initState에선 비동기 작업을 해선 안되기 때문에 다른 메서드에서 처리되도록 연기 시킬 것이다. void initState() { super.initState(); renderDogPic(); }
// 우린 Dog클래스 자체적으로 이미지를 가져오길 원하지만 // 이것은 플러터의 기본을 설명 할 수 있는 손쉬운 방법이다. (역자주: Dog클래스가 이미지를 가져오는게 맞겠지만 플러터의 기본을 설명하는데엔 아래 방식이 더 쉽다는것으로 이해됨.) void renderDogPic() async { // 서비스 호출 await dog.getImageUrl(); // setState는 플러터에게 변경된 모든것을 다시 렌더링 하도록 지시한다. // setState는 비동기가 될 수 없으므로 덮어 쓸 수 있는 변수를 사용한다. setState(() { renderUrl = dog.imageUrl; }); }
이제 우리는 렌더링 할 URL을 제대로 가져오는 아바타를 가지고 있다.
카드의 겹침(overlap) 모양을 얻으러면 내장된 위젯 Stack을 사용해야 한다. Stack 위젯은 가장 자리를 기준으로 자식들을 배치한다.
즉 CSS position의 top, bottom, left, right 속성과 동일하다.
Stack 내에서 Position위젯으로 자식들을 감쌀 수 있지만 꼭 할필요는 없다.
Position으로 감싸진 위젯은 웹 개발용어를 사용하자면 'document flow’의 외부에 있다.기본적으로 Stack위젯의 상단 모서리인 [0,0]위치에 있다.
감싸지지 않은 위젯은 배치되지 않는다.(positioned). 기본적으로 위젯의 열(column)으로 배치된 정상적인 'document flow’을 유지한다.
@override Widget build(BuildContext context) { // Start with a container so we can add layout and style props: return Container( // Arbitrary number that I decided looked good: height: 115.0, // A stack takes children, with a list of widgets. child: Stack( children: <Widget>[ // position our dog image, so we can explicitly place it. // We'll place it after we've made the card. Positioned( child: dogImage, ), ], ), ); }
Widget get dogCard { // 새로운 컨테이너 // height와 width는 스타일링을 위한 임의의 숫자이다. return Container( width: 290.0, height: 115.0, child: Card( color: Colors.black87, // padding처리를 위해 자식을 Padding위젯으로 감싼다. child: Padding( // padding을 제어하는 클래스를 EdgeInsets라고 한다. // EdgeInsets.only 생성자는 자식의 각 side에 명시적으로 padding을 설정하는데 사용. padding: const EdgeInsets.only( top: 8.0, bottom: 8.0, left: 64.0, ), // Columm은 또다른 레이아웃 위젯 - stack과 비슷 - // 자식들로 위젯의 목록을 취하고 위젯을 위에서 아래로 배치한다. child: Column( // 이들 정렬 속성은 CSS의 flexbox 속성과 정확하게 똑같이 작동한다. // column의 주축은 세로축이고 `MainAxisAlignment.spaceAround` 는 // CSS의 세로로 배치된 flexbox의 'justify-content: space-around'와 동일하다. crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Text(widget.dog.name, // 테마는 앱의 root에서 설정한 MaterialApp widget 이다. // 우리가 우리 소유의 것을 정하지 않았기 때문에 기본값을 가지고 있다. // 쉽게 변경 가능한 앱 차원의 일관된 스타일을 유지 하는데 탁월하다. style: Theme.of(context).textTheme.headline), Text(widget.dog.location, style: Theme.of(context).textTheme.subhead), Row( children: <Widget>[ Icon( Icons.star, ), Text(': ${widget.dog.rating} / 10') ], ) ], ), ), ), ); }
거의다 왔다!!, DogCard UI를 완성하기 위해 한 가지 더 할일이 있다.
build 메서드의 기본 위젯에 조금 더 스타일을 추가해 보자.