コードを書いているのに、表示されない。
解決したいこと
アプリ開発初心者でチャットGPTを使ってアプリを作っています。
グループ追加のボタンやテキストなどコードを書いているのにも関わらず、ビルドしても表示されません。どうしたらいいでしょうか?助けてください。
LINEのような個人、グループアカウントを作れるUIを作ってます。
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class GroupPage extends StatefulWidget {
final Function(String, ImageProvider) onGroupAdded;
GroupPage({required this.onGroupAdded});
@override
_GroupPageState createState() => _GroupPageState();
}
class _GroupPageState extends State<GroupPage> {
String _accountName = "ユーザー名"; // アカウント名の初期値
File? _profileImage; // プロフィール画像を保存する変数
List<Map<String, dynamic>> _friendsList = [];
List<Map<String, dynamic>> _groupsList = []; // 追加されたグループのリスト
List<int> _selectedFriendsForGroup = []; // グループ作成用に選択された友達のインデックス
Future<void> _pickImage() async {
final pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
setState(() {
_profileImage = File(pickedFile.path);
});
}
}
Future<File?> _pickGroupImage() async {
final pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
return File(pickedFile.path);
}
return null;
}
void _editProfile() {
TextEditingController _controller = TextEditingController(text: _accountName);
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Container(
height: MediaQuery.of(context).size.height * 0.9,
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: GestureDetector(
onTap: _pickImage,
child: CircleAvatar(
radius: 50,
backgroundImage: _profileImage != null
? FileImage(_profileImage!)
: AssetImage('assets/profile_placeholder.png') as ImageProvider,
),
),
),
SizedBox(height: 12),
Text('アカウント名', style: TextStyle(fontSize: 16)),
TextField(
controller: _controller,
autofocus: true,
decoration: InputDecoration(hintText: "新しいアカウント名を入力"),
),
SizedBox(height: 12),
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
_accountName = _controller.text;
});
Navigator.of(context).pop();
},
child: Text('保存'),
),
),
],
),
),
);
},
);
}
void _addFriend() {
setState(() {
_friendsList.add({
'name': '友達 ${_friendsList.length + 1}',
'image': AssetImage('assets/profile_placeholder.png'),
});
});
}
void _deleteFriend(int index) {
setState(() {
_friendsList.removeAt(index);
});
}
void _createGroup() {
_selectedFriendsForGroup.clear();
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text('グループに追加する友達を選択'),
content: Container(
width: double.maxFinite,
height: 300,
child: ListView.builder(
itemCount: _friendsList.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
radius: 20,
backgroundImage: _friendsList[index]['image'] is File
? FileImage(_friendsList[index]['image'])
: _friendsList[index]['image'] as ImageProvider,
),
title: Text(_friendsList[index]['name']),
trailing: Checkbox(
value: _selectedFriendsForGroup.contains(index),
onChanged: (bool? selected) {
setState(() {
if (selected == true) {
_selectedFriendsForGroup.add(index);
} else {
_selectedFriendsForGroup.remove(index);
}
});
},
),
);
},
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('キャンセル'),
),
TextButton(
onPressed: () {
if (_selectedFriendsForGroup.isNotEmpty) {
Navigator.of(context).pop();
_showGroupNameAndImageDialog();
}
},
child: Text('OK'),
),
],
);
},
);
},
);
}
void _showGroupNameAndImageDialog() {
TextEditingController groupNameController = TextEditingController();
File? groupProfileImage;
showDialog(
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text('グループ名と画像を設定してください'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () async {
groupProfileImage = await _pickGroupImage();
setState(() {});
},
child: CircleAvatar(
radius: 40,
backgroundImage: groupProfileImage != null
? FileImage(groupProfileImage!)
: AssetImage('assets/profile_placeholder.png') as ImageProvider,
),
),
SizedBox(height: 16),
TextField(
controller: groupNameController,
autofocus: true,
decoration: InputDecoration(hintText: 'グループ名を入力'),
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('キャンセル'),
),
TextButton(
onPressed: () {
if (groupNameController.text.isNotEmpty) {
List<Map<String, dynamic>> selectedFriends = _selectedFriendsForGroup
.map((index) => _friendsList[index])
.toList();
setState(() {
_groupsList.add({
'name': groupNameController.text,
'members': selectedFriends,
'image': groupProfileImage ?? AssetImage('assets/profile_placeholder.png'),
});
});
widget.onGroupAdded(
groupNameController.text,
groupProfileImage != null
? FileImage(groupProfileImage!)
: AssetImage('assets/profile_placeholder.png'),
);
Navigator.of(context).pop();
}
},
child: Text('作成'),
),
],
);
},
),
);
},
);
}
void _showGroupMembersWithDeleteDialog(int groupIndex) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('グループメンバー'),
content: Container(
width: double.maxFinite,
height: 300,
child: ListView.builder(
itemCount: _groupsList[groupIndex]['members'].length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
radius: 20,
backgroundImage: _groupsList[groupIndex]['members'][index]['image'] is File
? FileImage(_groupsList[groupIndex]['members'][index]['image'])
: _groupsList[groupIndex]['members'][index]['image'] as ImageProvider,
),
title: Text(_groupsList[groupIndex]['members'][index]['name']),
);
},
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
setState(() {
_groupsList.removeAt(groupIndex);
});
Navigator.of(context).pop();
},
child: Text('削除', style: TextStyle(color: Colors.red)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('支払いへ'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('閉じる'),
),
],
),
],
);
},
);
}
void _showFriendProfile(int index) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('プロフィール'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircleAvatar(
radius: 50,
backgroundImage: _friendsList[index]['image'] is File
? FileImage(_friendsList[index]['image'])
: _friendsList[index]['image'] as ImageProvider,
),
SizedBox(height: 16),
Text(
_friendsList[index]['name'],
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('閉じる'),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
double buttonDiameter = 80; // ボタンの直径を少し小さく変更
return Scaffold(
body: SafeArea(
child: SingleChildScrollView( // 全体を縦スクロール可能に
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), // 左右の余白を調整
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(color: Colors.grey.shade300, thickness: 1),
SizedBox(height: 30),
// プロフィール表示
Row(
children: [
CircleAvatar(
radius: 30,
backgroundImage: _profileImage != null
? FileImage(_profileImage!)
: AssetImage('assets/profile_placeholder.png') as ImageProvider,
),
SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_accountName,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)
),
TextButton(
onPressed: _editProfile,
child: Text('プロフィールを編集'),
),
],
),
],
),
SizedBox(height: 30),
// 友達追加ボタンと友達の人数を表示
Row(
children: [
Column(
children: [
ElevatedButton(
onPressed: _addFriend,
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(20), // ボタンを小さく
),
child: Text('+', style: TextStyle(fontSize: 35)), // アイコンも小さく調整
),
SizedBox(height: 8),
Container(
width: buttonDiameter,
alignment: Alignment.center,
child: Text(
'友達追加',
style: TextStyle(fontSize: 12),
),
),
],
),
Spacer(),
Text(
'友達: ${_friendsList.length}人', // 友達の人数を表示
style: TextStyle(fontSize: 16),
),
],
),
SizedBox(height: 30),
// 友達リストの表示
GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 一行に最大4つの要素を表示
childAspectRatio: 1, // 正方形
mainAxisSpacing: 10, // 上下間のスペース
crossAxisSpacing: 10, // 左右間のスペース
),
itemCount: _friendsList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_showFriendProfile(index); // 友達のプロフィールを表示する関数を呼び出す
},
onLongPress: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('友達を削除しますか?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('キャンセル'),
),
TextButton(
onPressed: () {
_deleteFriend(index);
Navigator.of(context).pop();
},
child: Text('削除'),
),
],
);
},
);
},
child: Column(
children: [
CircleAvatar(
radius: 25, // プロフィール画像を小さく
backgroundImage: _friendsList[index]['image'] is File
? FileImage(_friendsList[index]['image'])
: _friendsList[index]['image'] as ImageProvider,
),
SizedBox(height: 5),
Text(
_friendsList[index]['name'],
style: TextStyle(fontSize: 10),
),
],
),
);
},
),
SizedBox(height: 30),
// グレーのライン
Divider(color: Colors.grey.shade400, thickness: 1),
SizedBox(height: 20),
// グループ追加ボタンとグループ数を表示
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _createGroup, // グループ作成ダイアログを表示
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(20),
),
child: Text('+', style: TextStyle(fontSize: 35)),
),
SizedBox(height: 8),
Container(
width: buttonDiameter,
alignment: Alignment.center,
child: Text(
'グループ追加',
style: TextStyle(fontSize: 12),
),
),
],
),
Spacer(),
Text(
'グループ: ${_groupsList.length}個', // グループ数を表示
style: TextStyle(fontSize: 16),
),
],
),
SizedBox(height: 30),
// グループリストの表示
GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
childAspectRatio: 1,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: _groupsList.length,
itemBuilder: (context, index) {
if (_groupsList[index]['members'].isNotEmpty) {
return GestureDetector(
onTap: () {
_showGroupMembersWithDeleteDialog(index);
},
child: Column(
children: [
CircleAvatar(
radius: 25,
backgroundImage: _groupsList[index]['image'] is File
? FileImage(_groupsList[index]['image'])
: _groupsList[index]['image'] as ImageProvider,
),
SizedBox(height: 5),
Text(
_groupsList[index]['name'],
style: TextStyle(fontSize: 10),
),
],
),
);
} else {
return Container(); // メンバーがいないグループは表示しない
}
},
),
SizedBox(height: 30),
],
),
),
),
),
);
}
}