
import 'package:flutter/material.dart';
const rainbowColors = [
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.indigo,
Colors.purple,
];
import 'package:flutter/material.dart';
class MainLayout extends StatelessWidget {
final String title;
final Widget body;
const MainLayout({
required this.title,
required this.body,
Key? key,
}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: body,
);
}
}

SingleChildScrollView(
child: Column(
children: rainbowColors
.map(
(e) => renderContainer(color: e),
)
.toList(),
),
);

SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: [
renderContainer(color: Colors.black),
],
),
)
SingleChildScrollView(
clipBehavior: Clip.none,
physics: BouncingScrollPhysics(),
child: Column(
children: [
renderContainer(color: Colors.black),
],
),
)
SingleChildScrollView(
physics: BouncingScrollPhysics(),
// ClampingScrollPhySics() - 안드로이드 스타일
child: Column(
children: rainbowColors
.map(
(e) => renderContainer(color: e),
)
.toList(),
),
)
class SingleChildScrollViewScreen extends StatelessWidget {
final List<int> numbers = List.generate(100, (index) => index);
SingleChildScrollViewScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MainLayout(
title: 'SingleChildScrollView',
body: SingleChildScrollView(
child: Column(
children: numbers
.map(
(e) => renderContainer(
color: rainbowColors[e % rainbowColors.length],
index: e,
),
)
.toList(),
),
),
);
}

SingleChildScrollViewd에서는 child: Column(children:[])을 사용해야 했지만 ListView에서는 children에 바로 값을 넣을 수 있다.ListView(
children: numbers
.map(
(e) => renderContainer(
color: rainbowColors[e % rainbowColors.length],
index: e,
),
)
.toList(),
);

ListView.builder의 경우 화면에 출력된 위젯들만 반환된다.ListView.builder(
itemBuilder: (context, index) {
return renderContainer(
color: rainbowColors[index % rainbowColors.length],
index: index,
);
},
itemCount: 100,
);

ListView.separated(
itemBuilder: (context, index) {
return renderContainer(
color: rainbowColors[index % rainbowColors.length],
index: index,
);
},
separatorBuilder: (context, index) {
return renderContainer(
color: Colors.black, index: index, height: 100);
},
itemCount: 100,
)

crossAxisCount의 숫자에 따라 좌우에 배치되는 수가 달라진다.GridView.count(
crossAxisCount: 3,
children: numbers
.map(
(e) => renderContainer(
color: rainbowColors[e % rainbowColors.length],
index: e,
),
)
.toList(),
)

crossAxisSpacing, mainAxisSpacing을 사용해서 위젯들 간의 간격을 줄 수 있다.GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12.0,
mainAxisSpacing: 12.0,
),
itemBuilder: (context, index) {
return renderContainer(
color: rainbowColors[index % rainbowColors.length],
index: index,
);
},
)
위젯을 옮길 수 있는 ListView이다.
ListView에 onReorder 값이 추가 됬는데, onReorder에는 oldIndex와 newIndex를 파라미터를 받는 함수를 사용한다.
이 ListView는 위젯을 옮겨야 하기 때문에 위젯의 자리가 바뀌면서 기존에 있던 index가 새로운 index로 바뀐다.
index산정 방식에 대해 알아보자. 이 때 조건이 있는데 인덱스를 번호를 정할 때는 인덱스를 옮기기 전에 산정을 해야한다.
[a, b, c]에서a를c뒤로 옮긴다고 가정해보자
a는c다음인 인덱스 3번자리로 가게 된다.
이 과정에서oldIndex는0이 되고newIndex는3이 된다
산정 후 위치는[b, c, a]로 된다
[a, b, c]에서c를a앞으로 옮긴다고 가정해보자
c는 0번 인덱스 자리로 가게 된다.
이 과정에서oldIndex는2이 되고newIndex는0이 된다
산정 후 위치는[c, a, b]가 된다.실제 위치를 구하게 되면
oldIndex<newIndex조건에서newIndex에서-1을 하면 실제로 위젯이 이동한 위치이다
oldIndex>newIndex조건에서는newIndex를 그대로 사용하면 된다.
ReorderableListView(
children: numbers
.map(
(e) => renderContainer(
color: rainbowColors[e % rainbowColors.length], index: e),
)
.toList(),
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final item = numbers.removeAt(oldIndex);
numbers.insert(newIndex, item);
});
},
)
ReorderableListView에는 꼭 key를 넣어줘야 한다.Container를 사용하고 있다. 사람이 봤을 때는 색깔별로 다른 Container인 것을 인지하지만 시스템적으로 ReorderableListView 인지하지 못한다. 그래서 이것을 구분해주기 위해 key값을 넣어줘야 한다. 그리고 key값은 절대로 겹치지 않는 값을 넣어줘야 하기 때문에 index를 넣어준다.Widget renderContainer({
required Color color,
required int index,
double? height,
}) {
print(index);
return Container(
key: Key(index.toString()), // key
height: height ?? 300,
color: color,
child: Center(
child: Text(
index.toString(),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 30.0,
),
),
),
);
}
}

ReorderableListView.builder(
itemBuilder: (context, index) {
return renderContainer(
color: rainbowColors[index % rainbowColors.length], index: index);
},
itemCount: 100,
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final item = numbers.removeAt(oldIndex);
numbers.insert(newIndex, item);
});
},
)
class _ReorderalbeListViewScreenState extends State<ReorderalbeListViewScreen> {
List<int> numbers = List.generate(100, (index) => index);
Widget build(BuildContext context) {
return MainLayout(
title: 'ReorderalbeListViewScreen',
body: ReorderableListView.builder(
itemBuilder: (context, index) {
return renderContainer(
color: rainbowColors[numbers[index] % rainbowColors.length],
index: numbers[index],
);
},
itemCount: numbers.length,
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final item = numbers.removeAt(oldIndex);
numbers.insert(newIndex, item);
});
},
),
);
}
Widget renderContainer({
required Color color,
required int index,
double? height,
}) {
print(index);
return Container(
key: Key(index.toString()),
height: height ?? 300,
color: color,
child: Center(
child: Text(
index.toString(),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 30.0,
),
),
),
);
}
SliverAppBar(
pinned: true,
title: const Text('CustomScrollViewScreen'),
)
SliverAppBar(
floating: true,
title: const Text('CustomScrollViewScreen'),
)

floating: true 이여야 한다.SliverAppBar(
floating: true,
snap: true,
title: const Text('CustomScrollViewScreen'),
)

SliverAppBar(
stretch: true,
title: const Text('CustomScrollViewScreen'),
)

SliverAppBar(
expandedHeight: 200,
floating: true,
snap: true,
title: const Text('CustomScrollViewScreen'),
)

SliverAppBar(
expandedHeight: 200,
collapsedHeight: 200,
floating: true,
snap: true,
title: const Text('CustomScrollViewScreen'),
)
SliverAppBar(
expandedHeight: 200,
collapsedHeight: 150,
floating: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
background: Image.asset(
'asset/img/image_1.jpeg',
fit: BoxFit.cover,
),
title: Text("Hello"),
),
title: const Text('CustomScrollViewScreen'),
)

SliverChildListDelegate()를 사용한다.SliverList(
delegate: SliverChildListDelegate(
numbers
.map((e) => renderContainer(
color: rainbowColors[e % rainbowColors.length], index: e))
.toList(),
),
)

delegate에 SliverChildBuilderDelegate()를 사용하면 된다.SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => renderContainer(
color: rainbowColors[index % rainbowColors.length],
index: index),
),
)
SliverGrid(
delegate: SliverChildListDelegate(
numbers
.map((e) => renderContainer(
color: rainbowColors[e % rainbowColors.length], index: e))
.toList(),
),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
)

SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) => renderContainer(
color: rainbowColors[index % rainbowColors.length],
index: index),
childCount: 100,
),
gridDelegate:
SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 150),
)
RefreshIndicator(
onRefresh: () async {
await Future.delayed(
Duration(seconds: 3),
);
},
child: ListView(
children: numbers
.map(
(e) => renderContainer(
color: rainbowColors[e % rainbowColors.length], index: e),
)
.toList(),
),
)