
* 겪고 있는 문제 상황을 최대한 자세하게 작성해주세요.
* 문제 해결을 위해 어떤 시도를 해보았는지 구체적으로 함께 알려주세요.
안녕하세요.
숙제 답안을 보면서 vscode에 기입하며 학습중 에러가 자꾸 떠서 dartpad에 있는 답안 그대로 복사하여 붙혀넣어봤는데도
에러가 뜨네요.
숙제답안 전체 코드를 복사 하여 붙혀넣었을때 index와 animateTo 부분에서 에러가 뜨는걸로 나옵니다. 에러 코드는 이렇게 나왔습니다
: Error: Property 'index' cannot be accessed on 'TabController?' because it is potentially null.
main.dart:81
- 'TabController' is from 'package:flutter/src/material/tab_controller.dart' ('/C:/src/flutter/packages/flutter/lib/src/material/tab_controller.dart').
tab_controller.dart:1
Try accessing using ?. instead.
color: DefaultTabController.of(context).index == 1
^^^^^
: Error: Property 'index' cannot be accessed on 'TabController?' because it is potentially null.
main.dart:84
- 'TabController' is from 'package:flutter/src/material/tab_controller.dart' ('/C:/src/flutter/packages/flutter/lib/src/material/tab_controller.dart').
tab_controller.dart:1
Try accessing using ?. instead.
selectedColor: DefaultTabController.of(context).index == 1
^^^^^
: Error: Method 'animateTo' cannot be called on 'TabController?' because it is potentially null.
main.dart:339
- 'TabController' is from 'package:flutter/src/material/tab_controller.dart' ('/C:/src/flutter/packages/flutter/lib/src/material/tab_controller.dart').
tab_controller.dart:1
Try calling using ?. instead.
DefaultTabController.of(context).animateTo(0);
^^^^^^^^^
: Error: Method 'animateTo' cannot be called on 'TabController?' because it is potentially null.
main.dart:357
- 'TabController' is from 'package:flutter/src/material/tab_controller.dart' ('/C:/src/flutter/packages/flutter/lib/src/material/tab_controller.dart').
tab_controller.dart:1
Try calling using ?. instead.
DefaultTabController.of(context).animateTo(2);
^^^^^^^^^
그리고 추가적으로 숙제에대한 질문은 아니지만..
선 실습 후 이론 이라고 실습부터 해가며 천천히 이해하면 된다고 하셨는데
완전 생 초보이며 첫 코딩 입문인 제가 강의내용만으로 따라가려니 많이 어렵습니다. ㅜㅜ
강의는 꼼꼼하게 들으면서 문법도 따로 찾아서 영상보며 배워가고 있는데
막상 숙제답안과 같은 코드를 보면 이게 무엇인지 왜 이게 들어가는지 오류는 왜뜨는지
기본적으로 어떤 화면을 구현하고자 할 때 위젯의 단어가 어떤건지,
어떤 위젯을 써야하는지, 해당위젯의 하위로 무엇무엇이 있는지, 거기에 맞게 어떻게 문법을 작성해야하는지 등등
의문점들을 어떤식으로 구글링하여 찾아 풀어가는지, 단어를 구글링으로 찾아 알게되더라도 어떻게 작성하는지 코드들마다 작성법이 너무 다른거같아서
이해하기에 너무 어려움이있습니다. 너무 광범위해서 질문을 어떻게 드려야될지도 모르겠네요..
학습에 있어 좀 더 도움될만한 내용이 없을까요?
작성한 코드 및 에러 메세지
오류 발생 시, 작성한 코드 전체와 에러 메시지를 첨부해 주세요.
Tip 1) </> 아이콘을 눌러 코드박스를 만들어 보세요.
// ignore_for_file: prefer_const_constructors
// ignore_for_file: prefer_const_literals_to_create_immutables
// ignore_for_file: sized_box_for_whitespace
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Shazam',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 1,
length: 3,
child: Scaffold(
body: Stack(
children: [
TabBarView(
children: [
FirstTab(),
SecondTab(),
ThirdTab(),
],
),
SafeArea(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
child: Column(
children: [
Container(
alignment: Alignment.topCenter,
child: TabPage(),
),
],
),
),
),
],
),
),
);
}
}
class TabPage extends StatefulWidget {
const TabPage({super.key});
@override
State<TabPage> createState() => _TabPageState();
}
class _TabPageState extends State<TabPage> {
@override
Widget build(BuildContext context) {
return TabPageSelector(
color: DefaultTabController.of(context).index == 1
? Colors.blue[300]
: Colors.grey[400],
selectedColor: DefaultTabController.of(context).index == 1
? Colors.white
: Colors.blue,
indicatorSize: 8,
);
}
}
// 첫번째 페이지
class FirstTab extends StatelessWidget {
const FirstTab({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
const songs = [
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
];
return SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Icon(Icons.settings),
),
Text(
"라이브러리",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Icon(null),
],
),
SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
ImageIcon(
NetworkImage("https://i.ibb.co/hxNbZ8p/shazam.png"),
size: 18,
),
SizedBox(width: 12),
Text(
"Shazam",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
),
),
Divider(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
Icon(Icons.person_rounded),
SizedBox(width: 8),
Text(
"아티스트",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
),
),
Divider(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
Icon(Icons.music_note),
SizedBox(width: 8),
Text(
"회원님을 위한 재생목록",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
),
),
Divider(),
SizedBox(height: 16),
Text(
"최근 Shazam",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 16),
Expanded(
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 5,
),
itemCount: songs.length,
itemBuilder: (context, index) {
var song = songs[index];
String imageUrl = song['imageUrl']!;
String title = song['title']!;
String artist = song['artist']!;
index % 2 == 0; // 왼쪽, 1이면 오른쪽
return Container(
margin: EdgeInsets.only(
left: 4,
right: 4,
top: 4,
bottom: 4,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(8),
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 1,
spreadRadius: 1,
),
],
),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
child: Image.network(
imageUrl,
fit: BoxFit.cover,
height: MediaQuery.of(context).size.width *
0.5 *
5 /
3 *
0.55,
),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
artist,
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
Spacer(),
Image.network(
"https://i.ibb.co/KG9m5QS/applemusic.png",
width: 60,
),
SizedBox(height: 5),
],
),
),
)
],
),
);
},
),
)
],
),
),
);
}
}
// 두번째 페이지
class SecondTab extends StatelessWidget {
const SecondTab({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.blue[300]!, Colors.blue[900]!],
),
),
child: SafeArea(
child: Column(
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: Row(
children: [
GestureDetector(
onTap: () {
DefaultTabController.of(context).animateTo(0);
},
child: Column(
children: [
Icon(
Icons.person,
color: Colors.white,
),
Text(
"라이브러리",
style: TextStyle(color: Colors.white),
)
],
),
),
Spacer(),
GestureDetector(
onTap: () {
DefaultTabController.of(context).animateTo(2);
},
child: Column(
children: [
Icon(
Icons.show_chart,
color: Colors.white,
),
Text(
"차트",
style: TextStyle(color: Colors.white),
),
],
),
),
],
),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.1),
Text(
"Shazam하려면 탭하세요",
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.06),
Container(
alignment: Alignment.center,
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.blue[300],
shape: BoxShape.circle,
),
child: Image.network(
"https://i.ibb.co/hxNbZ8p/shazam.png",
color: Colors.white,
width: 130,
height: 130,
),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.12),
Container(
width: 50,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.blue[400],
shape: BoxShape.circle,
),
child: Icon(
Icons.search,
color: Colors.white,
size: 30,
),
)
],
),
),
);
}
}
// 세번째 페이지
class ThirdTab extends StatelessWidget {
const ThirdTab({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
const chartData = {
'korea': [
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
],
'global': [
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
],
'newyork': [
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
],
};
return SafeArea(
child: Column(
children: [
Text(
'차트',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
SizedBox(height: 16),
Expanded(
child: ListView(
children: [
Stack(
alignment: Alignment.center,
children: [
Container(
width: double.infinity,
height: 180,
color: Colors.purple[900],
),
Column(
children: [
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.white),
),
child: Text(
"국가 및 도시별 차트",
style: TextStyle(
color: Colors.purple[900],
fontWeight: FontWeight.bold,
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"전 세계",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
Container(
width: double.infinity,
height: 8,
color: Colors.grey[400],
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
'대한민국 차트',
style: TextStyle(fontSize: 16),
),
Spacer(),
Text(
'모두 보기',
style: TextStyle(color: Colors.blue),
),
],
),
),
Row(
children: [
...chartData['korea']!.map((song) {
// 스프레드 연산자: 리스트 등의 이터러블 안에 있는 요소를 분해한다.
String imageUrl = song['imageUrl']!;
String name = song['name']!;
String artist = song['artist']!;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
imageUrl,
width:
MediaQuery.of(context).size.width * 0.29,
),
Text(
name,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(artist),
],
),
);
}),
],
),
],
),
Container(
width: double.infinity,
height: 8,
color: Colors.grey[400],
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
'글로벌 차트',
style: TextStyle(fontSize: 16),
),
Spacer(),
Text(
'모두 보기',
style: TextStyle(color: Colors.blue),
),
],
),
),
Row(
children: [
...chartData['global']!.map((song) {
String imageUrl = song['imageUrl']!;
String name = song['name']!;
String artist = song['artist']!;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
imageUrl,
width:
MediaQuery.of(context).size.width * 0.29,
),
Text(
name,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(artist),
],
),
);
}),
],
),
],
),
Container(
width: double.infinity,
height: 8,
color: Colors.grey[400],
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
'뉴욕 차트',
style: TextStyle(fontSize: 16),
),
Spacer(),
Text(
'모두 보기',
style: TextStyle(color: Colors.blue),
),
],
),
),
Row(
children: [
...chartData['newyork']!.map((song) {
String imageUrl = song['imageUrl']!;
String name = song['name']!;
String artist = song['artist']!;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
imageUrl,
width:
MediaQuery.of(context).size.width * 0.29,
),
Text(
name,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(artist),
],
),
);
}),
],
),
],
),
],
),
),
],
),
);
}
}
