반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- MachineLearning
- localserver
- Chrome
- linux
- TensorFlow
- ReactNative
- react
- MAC
- centos
- VirtualBox
- vsCode
- jest
- 리눅스
- 개발
- PYTHON
- 오블완
- 티스토리챌린지
- 센토스
- build
- 네트워크
- 맥
- webpack
- unittest
- Android
- xcode
- androidstudio
- node
- qunit
- IOS
Archives
- Today
- Total
로메오의 블로그
Flutter 본문
반응형
$ brew --version
Homebrew 4.3.17
$ brew info openssl@1.1
==> openssl@1.1: stable 1.1.1w (bottled) [keg-only]
$ rvm --version
rvm 1.29.12 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]
$ ruby --version
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [arm64-darwin23]
$ pod --version
1.15.2
Navigator.push
main.dart
import 'package:flutter/material.dart';
import 'home_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Navigation Example',
home: HomeScreen(),
);
}
}
home_screen.dart
import 'package:flutter/material.dart';
import 'second_screen.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home Screen'),
),
// body
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
/**
* 1. Navigation Push
*/
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondScreen()),
);
},
child: const Text('1. Navigation Push'),
),
const SizedBox(height: 20),
/**
* 2. Full-size popover
*/
ElevatedButton(
onPressed: () {
Navigator.push(
context,
PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _, __) =>
const SecondScreen(),
transitionsBuilder:
(_, Animation<double> animation, __, Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
),
);
},
child: const Text('2. Full-Screen Popup'),
),
const SizedBox(height: 20),
/**
* 3. Dialog
*/
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
height: 400, // 원하는 높이로 설정
child: const SecondScreen(),
),
);
},
);
},
child: const Text('3. Dialog Screen'),
),
const SizedBox(height: 20),
/**
* 4. 파라미터 전달
*/
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SecondScreen(data: 'Hello from HomeScreen!'),
),
);
},
child: const Text('4. 파라미터 전달'),
),
const SizedBox(height: 20),
/**
* 5. print
*/
ElevatedButton(
onPressed: () {
// 콘솔에 메시지 출력
print('debug Message');
},
child: const Text('5. Print'),
),
const SizedBox(height: 20),
/**
* 6. Snack bar
*/
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('SnackBar Message'),
),
);
},
child: const Text('6. Show SnackBar'),
),
const SizedBox(height: 20),
/**
* 7. Stack 제거
*/
ElevatedButton(
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const SecondScreen()),
(Route<dynamic> route) => false, // 모든 기존 화면을 제거
);
},
child: const Text('7. Stack 제거'),
),
const SizedBox(height: 20),
],
),
),
);
}
}
second_screen.dart
import 'package:flutter/material.dart';
class SecondScreen extends StatelessWidget {
final String? data;
const SecondScreen({super.key, this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Center(
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.0),
),
padding: const EdgeInsets.all(20.0),
// child
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
data ?? 'This is Second Screen',
style: const TextStyle(
fontSize: 24.0, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Close'),
),
],
),
),
),
);
}
}
image picker 사용하기
dependencies:
flutter:
sdk: flutter
image_picker: ^0.8.4+8
pubspec.yaml 에 image_picker 패키지를 추가합니다.
$ flutter pub get
패키지를 설치합니다.
AndroidManifest.xml
your_flutter_project/
├── android/
│ ├── app/
│ │ ├── src/
│ │ │ ├── main/
│ │ │ │ └── AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
접근 권한을 추가합니다.
Info.plist
your_flutter_project/
├── ios/
│ ├── Runner/
│ │ └── Info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>사진첩 접근을 허용해주세요.</string>
다국어
dependencies:
flutter:
sdk: flutter
image_picker: ^0.8.4+8
flutter_localizations:
sdk: flutter
intl: ^0.19.0
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
intl_utils: ^2.0.0
lib > l10n > intl_en.arb
lib > l10n > intl_ko.arb
파일을 생성합니다.
intl_en.arb
{
"hello": "Hello",
"welcome": "Welcome to Flutter",
"Home_Screen": "Home Screen 1"
}
intl_ko.arb
{
"hello": "안녕하세요",
"welcome": "플러터에 오신 것을 환영합니다",
"Home_Screen": "홈 스크린 1"
}
$ flutter pub run intl_utils:generate
arb 파일을 generate 합니다.
lib > generated 폴더에 dart 파일이 generate 됩니다.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'generated/l10n.dart';
import 'home_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
// locale: const Locale('en'),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
title: 'Navigation Example',
home: HomeScreen(),
);
}
}
home_screen.dart
...
import 'generated/l10n.dart';
class HomeScreen extends StatefulWidget {
...
}
class _HomeScreenState extends State<HomeScreen> {
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(S.of(context).Home_Screen),
),
...
);
}
}
http 통신
dependencies:
flutter:
sdk: flutter
image_picker: ^0.8.4+8
flutter_localizations:
sdk: flutter
intl: ^0.19.0
http: ^0.13.3
http_screen.dart
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'models/post.dart';
class HttpScreen extends StatefulWidget {
const HttpScreen({super.key});
@override
// ignore: library_private_types_in_public_api
_HttpPageState createState() => _HttpPageState();
}
class _HttpPageState extends State<HttpScreen> {
List<Post> _posts = [];
bool _isLoading = false;
Future<void> fetchPosts() async {
setState(() {
_isLoading = true;
});
final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
if (response.statusCode == 200) {
List<dynamic> body = json.decode(response.body);
List<Post> posts =
body.map((dynamic item) => Post.fromJson(item)).toList();
setState(() {
_posts = posts;
_isLoading = false;
});
} else {
setState(() {
_isLoading = false;
});
throw Exception('Failed to load posts');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('http 통신'),
),
// body
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
fetchPosts();
},
child: const Text('Fetch Data'),
),
const SizedBox(height: 20),
Expanded(
child: _isLoading
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: _posts.length,
itemBuilder: (context, index) {
Post post = _posts[index];
return ListTile(
title: Text(post.title),
subtitle: Text(post.body),
);
},
),
)
],
),
));
}
}
libs > models > post.dart
class Post {
final int id;
final String title;
final String body;
Post({required this.id, required this.title, required this.body});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
사진업로드
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'dart:io';
class GalleryScreen extends StatefulWidget {
const GalleryScreen({super.key});
@override
// ignore: library_private_types_in_public_api
_GalleryPageState createState() => _GalleryPageState();
}
class _GalleryPageState extends State<GalleryScreen> {
File? _image;
Future<void> _pickImage() async {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
setState(() {
_image = File(pickedFile.path);
});
}
}
Future<void> _uploadImage() async {
if (_image == null) {
print('No image to upload.');
return;
}
String uploadUrl =
'https://yoursite.com/upload.php'; // 서버의 업로드 URL
try {
var request = http.MultipartRequest('POST', Uri.parse(uploadUrl));
request.files
.add(await http.MultipartFile.fromPath('image', _image!.path));
var response = await request.send();
if (response.statusCode == 200) {
print('Image uploaded successfully.');
var responseData = await response.stream.toBytes();
var responseString = String.fromCharCodes(responseData);
print(responseString);
} else {
print('Image upload failed with status: ${response.statusCode}');
}
} catch (e) {
print('Error uploading image: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('사진 불러오기'),
),
// body
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_image == null ? const Text('이미지를 선택하세요.') : Image.file(_image!),
const SizedBox(height: 20),
/**
* 사진 불러오기
*/
ElevatedButton(
onPressed: _pickImage,
child: const Text('사진첩에서 이미지 선택'),
),
const SizedBox(height: 20),
/**
* 사진 전송
*/
ElevatedButton(
onPressed: _uploadImage,
child: const Text('사진 전송'),
),
],
),
));
}
}
upload.php
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_FILES['image'])) {
$file = $_FILES['image'];
$upload_directory = __DIR__ . '/uploads/';
$filename = basename($file['name']);
$target_path = $upload_directory . $filename;
// 업로드 디렉토리 쓰기 가능 여부 확인
if (!is_writable($upload_directory)) {
echo json_encode(array('status' => 'error', 'message' => 'Upload directory is not writable.'));
exit;
}
// 파일 이동 시도
if (move_uploaded_file($file['tmp_name'], $target_path)) {
echo json_encode(array('status' => 'success', 'message' => 'File uploaded successfully.'));
} else {
echo json_encode(array('status' => 'error', 'message' => 'Failed to move uploaded file.'));
// 오류 메시지를 출력
$error = error_get_last();
echo json_encode(array('status' => 'error', 'error' => $error));
}
} else {
echo json_encode(array('status' => 'error', 'message' => 'No file uploaded.'));
}
} else {
echo json_encode(array('status' => 'error', 'message' => 'Invalid request method.'));
}
?>
php version 5.3
반응형
'App & OS > Hybrid' 카테고리의 다른 글
Flutter in app 결제 구현 (0) | 2024.11.22 |
---|---|
Flutter Firebase login (4) | 2024.09.08 |
Flutter hello world (0) | 2024.08.15 |
[React Native] Postman에서 Push 보내기 (0) | 2019.08.04 |
[React Native] Firebase Push Notification 추가 [Android] (0) | 2019.07.29 |
Comments