[Flutter] ScrollController로 Container 제어하기

김호정's avatar
Oct 03, 2024
[Flutter] ScrollController로 Container 제어하기
 
6장인가 7장인가 그 로그인쪽에 스크롤 적용!!!!
 
textformfield는 터치했을때 스크롤 조금씩 내려오도록 해야한다.
 
안그러면 불편해서 사용자가 안쓴다.
 
 

선생님 공유

 
 

구글링 참고

 
 
 
선생님 코드 보고 하나씩 해보는데
 
notion image
 
HomePage 를 StatefulWidget으로 뺏다.
 
 
일단 stl 로 만들고 나서 stf 로 alt enter 해서 바꿈
 
 
notion image
 
stf 로 바꾸면 상태를 만드는 createSatete 가 자동으로 오버라이딩 되고,
ScrollController 를 불러내서 이벤트리스너를 달아줌 ( addListener )
 
addListener 로 들어가보니 void 여서 return을 안하니까 () {} 이렇게 익명함수로 써줌
 
그리고 어떤 함수가 실행되면 listen 하게 할건지 설정하고
 
scrollListener 함수를 만들어줌
 
notion image
 
여기까지 해주고 스크롤링을 web에서 하면 print가 찍힌다.
 
이제 스크롤할 때, 위의 good 컨테이너의 크기가 줄어들도록 offset을 활용해보자
 
notion image
notion image
 
스크롤을 내릴수록 offset이 커진다.
 
offset이 300 보다 작은 경우 ( 페이지 상단에 있을 때 )
 
Container 의 높이를 줄이는 효과를 적용해야 한다.
height = 300 - currentOffset; // Container의 height 가 동적으로 변한다
 
현재 offset을 기준으로 Container의 height를 조금씩 작게 줄인다.
height = 56; // Container 가 소멸되지않게? 최소 높이 설정
 
Container 가 너무 작아지지 않게 최소 높이를 설정해준다.
 
 
notion image
 
 
default가 0 인 prev 랑 curentOffset 을 이용해서
 
스크롤이 위로 올라가는지 아래로 내려가는지 감지하고,
 
가장 상단, 가장 하단에 닿은 것도 감지한다.
 
→ 막줄에 prev = currentOffset 을 넣어줘야지
 
직전의 offset 과 지금 변화하는 currentOffset 을 if 문으로 비교해서
 
효과를 줄 수 있다.
 
notion image
 
이렇게 잘 찍히고,
 
notion image
 
상단 Container의 크기도 아래로 스크롤 하면
 
notion image
 
이렇게 작아진다 ( 최소 높이 56 )
 
 
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); // 상태를 만듦 } class _HomePageState extends State<HomePage> { final ScrollController _controller = ScrollController(); double prev = 0; double height = 300; @override void initState() { _controller.addListener((){ scrollListener(); }); // 익명 super.initState(); } void scrollListener(){ print("스크롤 동작중"); double currentOffset = _controller.offset; //bool keepScrollOffset = _controller.keepScrollOffset; print("currentOffset : $currentOffset"); //print("keepScrollOffset : $keepScrollOffset"); // 실습 if(currentOffset < 300) { setState(() { height = 300 - currentOffset; // Container의 height 가 동적으로 변한다 if(height < 56) { height = 56; // Container 가 소멸되지않게? 최소 높이 설정 } }); } // 301 0 if(currentOffset > prev) { print("아래로 내려가요"); } // if(currentOffset < prev) { print("위로 올라가요"); } if(currentOffset == _controller.position.maxScrollExtent){ print("가장 하단"); } if(currentOffset == _controller.position.minScrollExtent){ print("가장 상단"); } prev = currentOffset; } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( children: [ Container( color: Colors.red, height: height, width: double.infinity, child: Center( child: Text( "Good", style: TextStyle(color: Colors.white, fontSize: height / 3), ), ), ), Expanded( child: ListView.builder( controller: _controller, itemCount: 100, itemBuilder: (context, index) => Text("제목 $index"), ), ), ], ), ), ); } }
 
 
 
 
 
 
notion image
 
스크롤 할 때 상단의 Container를 투명하게 만들고 싶으면
opacity 도 같이 계산해서 Container의 color 매개변수로 넣어준다.
 
notion image
currentOffset이 100인 경우 → 2 / 3 , 200인 경우 → 1 / 3
 
내려갈수록 0 에 가까워져서 투명해진다.
 
opacity : 0~1.0 사이의 값을 가지고 불투명도를 지정하는데
0일 경우 가장 투명하고, 1일 경우 가장 불투명하게 보인다.
notion image
 
Container 의 color를 지정할 때, opacity 도 같이 줄려면
 
Colors 말고 Color.fromRGBO 를 사용해서 Red 컬러 + opacity (동적으로 변경됨) 를 같이 준다.
 
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); // 상태를 만듦 } class _HomePageState extends State<HomePage> { final ScrollController _controller = ScrollController(); double prev = 0; double height = 300; // 추가 double op = 1.0; @override void initState() { _controller.addListener((){ scrollListener(); }); // 익명 super.initState(); } void scrollListener(){ print("스크롤 동작중"); double currentOffset = _controller.offset; //bool keepScrollOffset = _controller.keepScrollOffset; print("currentOffset : $currentOffset"); //print("keepScrollOffset : $keepScrollOffset"); // 실습 if(currentOffset < 300) { setState(() { height = 300 - currentOffset; // Container의 height 가 동적으로 변한다 op = (300 - currentOffset) / 300; // co가 100이면 2 / 3 , 200 이면 1 / 3 -> 내려갈수록 0 에 가까워져서 투명해진다. if(height < 56) { height = 56; // Container 가 소멸되지않게? 최소 높이 설정 } }); } // 301 0 if(currentOffset > prev) { print("아래로 내려가요"); } // if(currentOffset < prev) { print("위로 올라가요"); } if(currentOffset == _controller.position.maxScrollExtent){ print("가장 하단"); } if(currentOffset == _controller.position.minScrollExtent){ print("가장 상단"); } prev = currentOffset; } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( children: [ Container( color: Color.fromRGBO(255, 0, 0, op), height: height, width: double.infinity, child: Center( child: Text( "Good", style: TextStyle(color: Colors.white, fontSize: height / 3), ), ), ), Expanded( child: ListView.builder( controller: _controller, itemCount: 100, itemBuilder: (context, index) => Text("제목 $index"), ), ), ], ), ), ); } }
 
notion image
 
가장 위에 있을 땐 255, 0, 0, 1 (opacity 1.0임) 의 real red 색
 
notion image
 
조금 내리면 height 도 변경되고,
 
opacity도 만약 currentOffset이 100 이라 치면,
 
2 / 3 ⇒ 0.6
 
255, 0, 0, 0,6 이 되어서 조금 더 투명해졌다.
 
op = (300 - currentOffset) / 300;
 
으로 설정해두었기 때문에,
 
currentOffset이 300 이상으로 넘어가면
 
op = 0 이 되어서
 
최대치로 투명해진다.
 
notion image
 
 
Share article

keepgoing