이렇게 더보기 칸을 클릭하면 헤드라인 TOP20 뉴스를 보여줄 페이지로 이동하게끔 구현하는 것을 진행해 보려고 한다.
우선 더보기 버튼은 이미 만들어 주었기 때문에 생략하고 더보기 이후 어떤 로직들이 발생하는지 알아보자
Center(
child: TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NewsDetailPage()));
},
child: Text("더보기",
style: TextStyle(
fontSize: textSizeSmall2,
color: isDarkMode ? Colors.white : Colors.black)),
),
)
더보기를 누르면 NewsDetailPage()로 이동한다.
[NewDetailPage()]
api 로 불러온 값 중 이미지가 들어가지 않는 경우가 있어 오류가 떴었다. 오류를 찾던 도중 이미지를 불러올 때 http, https와 같은 경로 오류인 것으로 확인되었고 해당하는 조건 처리를 하기와 같이 해주었다.
if (newsList[index]
.urlToImage!
.startsWith('http') ||
newsList[index]
.urlToImage!
.startsWith('https'))
그래서 이미지 불러오는 코드가 좀 복잡한데 나름 잘 끝냈다고 생각한다,,, ,아님 말구 ㅠ
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import '../color/Colors.dart';
import '../comm/constants.dart';
import '../news/News.dart';
/////////////////////////
////뉴스 - 더보기 페이지////
////////////////////////
class NewsDetailPage extends StatelessWidget {
final isDarkMode = Get.isDarkMode;
@override
Widget build(BuildContext context) {
requestNews();
var deviceHeight = MediaQuery.of(context).size.height;
var deviceWidth = MediaQuery.of(context).size.width;
return WillPopScope(
onWillPop: () async {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.black.withAlpha(50)));
Navigator.pop(context, true);
return false;
},
child: Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Container(
height: 60,
padding: EdgeInsets.fromLTRB(15, 10, 15, 0),
alignment: Alignment.centerLeft,
child: Row(
children: [
InkWell(
child: Icon(Icons.keyboard_arrow_left_sharp, size: 30),
onTap: () {
Get.back();
},
),
Text('뉴스',
style: TextStyle(
fontSize: textSizeTitle,
fontWeight: FontWeight.bold)),
],
)),
SizedBox(height: 20),
FutureBuilder<List<News>>(
future: requestNews(),
builder: (context, AsyncSnapshot<List<News>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text("데이터를 가져오는 동안 오류가 발생했습니다."));
} else {
List<News> newsList = snapshot.data ?? [];
if (newsList.isEmpty) {
return Center(child: Text("뉴스가 없습니다."));
}
List<Widget> newsContainers = [];
for (int index = 0; index < newsList.length; index++) {
newsContainers.add(
Container(
width: isBigScreen ? deviceWidth * 0.6 : null,
padding: EdgeInsets.fromLTRB(10, 5, 25, 5),
margin: EdgeInsets.fromLTRB(10, 5, 10, 15),
decoration: BoxDecoration(
color: isDarkMode ? DarkColors.gray4 : Colors.white,
borderRadius: BorderRadius.circular(10.0),
boxShadow: [
BoxShadow(
color: DarkColors.gray3.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 1,
offset:
Offset(2, 2), // changes position of shadow
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
child: Row(
children: [
Container(
padding: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(30.0)),
// decoration : Boxde borderRadius: BorderRadius.circular(30.0),
child: (() {
if (newsList[index].urlToImage !=
null &&
newsList[index]
.urlToImage!
.isNotEmpty) {
if (newsList[index]
.urlToImage!
.startsWith('http') ||
newsList[index]
.urlToImage!
.startsWith('https')) {
return Image.network(
newsList[index].urlToImage!,
width: 90,
height: 90,
errorBuilder:
(context, error, stackTrace) {
return Image.asset(
'lib/assets/images/News.png',
width: 90,
height: 90,
fit: BoxFit.cover,
);
},
);
} else {
return Image.asset(
'lib/assets/images/News.png',
width: 90,
height: 90,
fit: BoxFit.cover,
);
}
}
// URL이 없는 경우\
return Image.asset(
'lib/assets/images/News.png',
width: 90,
height: 90,
fit: BoxFit.cover,
);
})(),
),
SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
newsList[index].title,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
SizedBox(height: 10),
Text(
_formatPublishedDate(
newsList[index].publishedAt),
style: TextStyle(
fontSize: textSizeSmall),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
],
),
),
],
),
onTap: () async {
newsList[index].urlToImage != null
? await launchUrl(
Uri.parse(newsList[index].url))
: Text('뉴스 자세히 보기가 불가능합니다');
},
),
],
),
),
);
}
return Column(children: newsContainers);
}
}),
SizedBox(height: 20),
],
))),
),
);
}
String _formatPublishedDate(String dateTimeString) {
DateTime dateTime = DateTime.parse(dateTimeString);
return '${dateTime.year}-${_twoDigits(dateTime.month)}-${_twoDigits(dateTime.day)}';
}
String _twoDigits(int n) => n.toString().padLeft(2, '0');
Future<List<News>> requestNews() async {
Uri uri = Uri.parse(
"https://newsapi.org/v2/top-headlines?country=kr&category=business&apiKey=765547ccb99a4d9991a6de66249d67ca");
List<News> news = [];
final response = await http.get(uri);
if (response.statusCode == 200) {
List<dynamic> articles = jsonDecode(response.body)['articles'];
news = articles.map<News>((article) {
return News.fromMap(article);
}).toList();
}
return news;
}
}
'Flutter' 카테고리의 다른 글
Flutter TextField : hintText 맨 위로 배치하기 (0) | 2024.02.27 |
---|---|
Table Class 의 Cell 각각의 너비 및 높이 지정하기 (0) | 2024.02.26 |
Flutter 뉴스 API 연동하기(3) - 화면에 그리기 (0) | 2024.02.01 |
Flutter 뉴스 API 연동하기(2) - 통신하기 (0) | 2024.01.31 |
Flutter 뉴스 API 연동하기(1) - 기초 세팅 (0) | 2024.01.28 |