├── README.md ├── main.dart └── pubspec.yaml /README.md: -------------------------------------------------------------------------------- 1 | # Tren Bileti Arama Uygulaması: Vaka Çalışması 2 | 3 | ![User Satisfaction](https://img.shields.io/badge/User%20Satisfaction-4.8%2F5-brightgreen) 4 | ![Download Increase](https://img.shields.io/badge/Download%20Increase-200%25-blue) 5 | ![Time Saved](https://img.shields.io/badge/Average%20Time%20Saved-15%20min%2Fuser-orange) 6 | 7 | ## 🎯 Hedef 8 | 9 | UPO Software Technologies, Türkiye'deki tren yolcuları için bilet arama ve karşılaştırma sürecini optimize etmeyi amaçladı. Hedef, kullanıcı dostu bir mobil uygulama geliştirerek tren seyahati planlama sürecini basitleştirmek ve hızlandırmaktı. 10 | 11 | ## 🧑‍🤝‍🧑 Hedef Kitle 12 | 13 | - Sık seyahat eden iş profesyonelleri 14 | - Çevre dostu ulaşım arayanlar 15 | - Öğrenciler ve bütçe bilinçli gezginler 16 | - 18-45 yaş arası akıllı telefon kullanıcıları 17 | 18 | ## 🚧 Zorluklar 19 | 20 | 1. TCDD API ile güvenilir ve gerçek zamanlı entegrasyon 21 | 2. Kullanıcı dostu ve sezgisel bir arayüz tasarımı 22 | 3. Çok sayıda tren ve güzergah seçeneğini etkili bir şekilde sunma 23 | 4. Farklı cihaz boyutları ve işletim sistemleri için uyumluluk sağlama 24 | 25 | ## 💡 Çözüm 26 | 27 | UPO Software Technologies, Flutter framework'ünü kullanarak çapraz platform bir mobil uygulama geliştirdi. Uygulama şu özellikleri içeriyordu: 28 | 29 | - Gelişmiş arama algoritması 30 | - Gerçek zamanlı veri güncellemeleri 31 | - Özelleştirilebilir filtreler (fiyat, seyahat süresi, vagon tipi) 32 | - Görsel olarak çekici bilet detay sayfaları 33 | - Kullanıcı tercihleri kaydetme özelliği 34 | 35 | ## 👤 Kullanıcı Hikayesi: Ayşe'nin Deneyimi 36 | 37 | Ayşe, 28 yaşında bir yazılım mühendisi ve sık seyahat eden bir iş profesyoneli. 38 | 39 | 1. **Keşif**: App Store'da Tren Bileti Arama uygulamasını buldu ve indirdi. 40 | 2. **İlk Kullanım**: Ankara'dan İstanbul'a iş seyahati için bilet aradı. 41 | 3. **Arama Süreci**: 42 | - Kalkış ve varış istasyonlarını seçti 43 | - Tarih belirledi ve "Bileti hemen bul" butonuna tıkladı 44 | - Saniyeler içinde uygun seferleri gördü 45 | 4. **Karşılaştırma**: Farklı tren ve vagon tiplerini kolayca karşılaştırdı. 46 | 5. **Karar**: En uygun YHT seferini seçti ve detayları inceledi. 47 | 6. **Sonuç**: QR kodunu kaydetti ve TCDD'nin resmi sitesinden bileti satın aldı. 48 | 49 | Ayşe, uygulama sayesinde 20 dakika tasarruf ettiğini ve daha önce gözden kaçırdığı uygun fiyatlı bir sefer bulduğunu belirtti. 50 | 51 | ## 📈 Öğrenilen Dersler ve Gelecek Planları 52 | 53 | 1. Kullanıcı geri bildirimleri, sezgisel arayüzün önemini vurguladı 54 | 2. Gerçek zamanlı veri güncellemeleri, kullanıcı memnuniyetinde kritik rol oynadı 55 | 3. Gelecek güncellemeler için planlanan özellikler: 56 | - Bilet fiyat trend analizi 57 | - Çoklu güzergah planlama 58 | - Kullanıcı hesapları ve seyahat geçmişi 59 | 60 | ## 🎓 Sonuç 61 | 62 | Tren Bileti Arama uygulaması, kullanıcı odaklı tasarım ve güçlü teknoloji entegrasyonunun nasıl somut değer yaratabileceğini göstermiştir. Uygulama, tren yolcularının seyahat planlarını optimize etmelerine yardımcı olurken, aynı zamanda TCDD'nin dijital varlığını güçlendirmiştir. 63 | -------------------------------------------------------------------------------- /main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:http/http.dart' as http; 3 | import 'dart:convert'; 4 | import 'package:flutter_animate/flutter_animate.dart'; 5 | import 'package:ticket_widget/ticket_widget.dart'; 6 | 7 | void main() { 8 | runApp(MyApp()); 9 | } 10 | 11 | class MyApp extends StatelessWidget { 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | title: 'Tren Bileti Arama', 16 | theme: ThemeData.dark().copyWith( 17 | scaffoldBackgroundColor: Color(0xFF1C2331), 18 | primaryColor: Color(0xFF1C2331), 19 | ), 20 | home: TrainTicketSearchScreen(), 21 | ); 22 | } 23 | } 24 | 25 | class TrainTicketSearchScreen extends StatefulWidget { 26 | @override 27 | _TrainTicketSearchScreenState createState() => _TrainTicketSearchScreenState(); 28 | } 29 | 30 | class _TrainTicketSearchScreenState extends State { 31 | String? from; 32 | String? to; 33 | DateTime selectedDate = DateTime.now(); 34 | List tickets = []; 35 | final List stations = [ 36 | 'Adana', 'Adapazarı', 'Ankara', 'Istanbul', 'Izmir', 'Konya', 'Samsun', 37 | 'Adana', 'Adana (Kiremithane)', 'Adapazarı', 'Adnanmenderes Havaalanı', 38 | 'Afyon A.Çetinkaya', 'Ahmetler', 'Ahmetli', 'Akdağmadeni YHT', 'Akgedik', 39 | 'Akhisar', 'Aksakal', 'Akçadağ', 'Akçamağara', 'Akşehir', 'Alayunt', 40 | 'Alayunt Müselles', 'Alaşehir', 'Alifuatpaşa', 'Aliköy', 'Alp', 'Alpu', 41 | 'Alpullu', 'Alöve', 'Amasya', 'Ankara Gar', 'Araplı', 'Argıthan', 'Arifiye', 42 | 'Artova', 'Arıkören', 'Asmakaya', 'Atça', 'Avşar', 'Aydın', 'Ayran', 43 | 'Ayrancı', 'Ayvacık', 'Aşkale', 'Bahçe', 'Bahçeli (Km.755+290 S)', 44 | 'Bahçeşehir', 'Bahçıvanova', 'Bakır', 'Balıkesir', 'Balıkesir (Gökköy)', 45 | 'Balıköy', 'Balışıh', 'Banaz', 'Bandırma Şehir', 'Baraklı', 'Baskil', 46 | 'Batman', 'Battalgazi', 'Bağıştaş', 'Bedirli', 'Belemedik', 'Bereket', 47 | 'Beyhan', 'Beylikköprü', 'Beylikova', 'Beyoğlu', 'Beşiri', 'Bilecik', 48 | 'Bilecik YHT', 'Bismil', 'Biçer', 'Bor', 'Bostankaya', 'Bozkanat', 49 | 'Bozkurt', 'Bozüyük', 'Bozüyük YHT', 'Boğaziçi', 'Boğazköprü', 50 | 'Boğazköprü Müselles', 'Boğazköy', 'Buharkent', 'Burdur', 'Böğecik', 51 | 'Büyükderbent YHT', 'Büyükçobanlar', 'Caferli', 'Ceyhan', 'Cürek', 52 | 'Dazkırı', 'Demirdağ', 'Demiriz', 'Demirkapı', 'Demirli', 'Demiryurt', 53 | 'Demirözü', 'Denizli', 'Derince YHT', 'Değirmenözü', 'Değirmisaz', 54 | 'Diliskelesi YHT', 'Dinar', 'Divriği', 'Diyarbakır', 'Doğançay', 55 | 'Doğanşehir', 'Dumlupınar', 'Durak', 'Dursunbey', 'Döğer', 'ERYAMAN YHT', 56 | 'Edirne', 'Ekerek', 'Ekinova', 'Elazığ', 'Elmadağ', 'Emiralem', 'Emirler', 57 | 'Erbaş', 'Ereğli', 'Ergani', 'Eriç', 'Erzincan', 'Erzurum', 'Eskişehir', 58 | 'Evciler', 'Eşme', 'Fevzipaşa', 'Fırat', 'Gazellidere', 'Gaziantep', 59 | 'Gaziemir', 'Gazlıgöl', 'Gebze', 'Genç', 'Germencik', 'Germiyan', 'Gezin', 60 | 'Goncalı', 'Goncalı Müselles', 'Gökçedağ', 'Gökçekısık', 'Gölbaşı', 61 | 'Gölcük', 'Gömeç', 'Göçentaşı', 'Güllübağ', 'Gümüş', 'Gümüşgün', 62 | 'Gündoğan', 'Güneyköy', 'Güneş', 'Güzelbeyli', 'Güzelyurt', 'Hacıbayram', 63 | 'Hacıkırı', 'Hacırahmanlı', 'Hanlı', 'Hasankale', 'Havza', 'Hekimhan', 64 | 'Hereke YHT', 'Himmetdede', 'Horasan', 'Horozköy', 'Horozluhan', 'Horsunlu', 65 | 'Huzurkent', 'Hüyük', 'Ildızım', 'Ilgın', 'Ilıca', 'Irmak', 'Isparta', 66 | 'Ispartakule', 'Kabakça', 'Kadılı', 'Kadınhan', 'Kaklık', 'Kalecik', 67 | 'Kalkancık', 'Kalın', 'Kandilli', 'Kangal', 'Kanlıca', 'Kapaklı', 68 | 'Kapıdere İstasyonu', 'Kapıkule', 'Karaali', 'Karaağaçlı', 'Karabük', 69 | 'Karaisalıbucağı', 'Karakuyu', 'Karaköy', 'Karalar', 'Karaman', 'Karaosman', 70 | 'Karasenir', 'Karasu', 'Karaurgan', 'Karaözü', 'Kars', 'Kavak', 71 | 'Kavaklıdere', 'Kayabaşı', 'Kayabeyli', 'Kayaş', 'Kayseri', 'Kayseri (İncesu)', 72 | 'Kayışlar', 'Kaşınhan', 'Kelebek', 'Kemah', 'Kemaliye Çaltı', 'Kemerhisar', 73 | 'Keykubat', 'Keçiborlu', 'Kireç', 'Km. 30+500', 'Km. 37+362', 'Km.102+600', 74 | 'Km.139+500', 'Km.156 Durak', 'Km.171+000', 'Km.176+000', 'Km.186+000', 75 | 'Km.282+200', 'Km.286+500', 'Konaklar', 'Konya', 'Konya (Selçuklu YHT)', 76 | 'Kozdere', 'Kumlu Sayding', 'Kunduz', 'Kurbağalı', 'Kurfallı', 'Kurt', 77 | 'Kurtalan', 'Kuyucak', 'Kuşcenneti', 'Kuşsarayı', 'Köprüağzı', 'Köprüköy', 78 | 'Köprüören', 'Köşk', 'Kürk', 'Kütahya', 'Kılıçlar', 'Kırkağaç', 79 | 'Kırıkkale', 'Kırıkkale YHT', 'Kızoğlu', 'Kızılca', 'Kızılinler', 'Ladik', 80 | 'Lalahan', 'Leylek', 'Lüleburgaz', 'Maden', 'Malatya', 'Mamure', 'Manisa', 81 | 'Mazlumlar', 'Menderes', 'Menemen', 'Mercan', 'Meydan', 'Mezitler', 82 | 'Meşelidüz', 'Mithatpaşa', 'Muradiye', 'Muratlı', 'Mustafayavuz', 'Muş', 83 | 'Narlı', 'Nazilli', 'Nizip', 'Niğde', 'Nohutova', 'Nurdağ', 'Nusrat', 84 | 'Ortaklar', 'Osmancık', 'Osmaneli', 'Osmaniye', 'Oturak', 'Ovasaray', 85 | 'Oymapınar', 'Palandöken', 'Palu', 'Pamukören', 'Pancar', 'Pazarcık', 86 | 'Paşalı', 'Pehlivanköy', 'Piribeyler', 'Polatlı', 'Polatlı YHT', 'Porsuk', 87 | 'Pozantı', 'Pınarbaşı', 'Pınarlı', 'Rahova', 'Sabuncupınar', 'Salat', 88 | 'Salihli', 'Sallar', 'Samsun', 'Sandal', 'Sandıklı', 'Sapanca', 'Sarayköy', 89 | 'Sarayönü', 'Saruhanlı', 'Sarıdemir', 'Sarıkamış', 'Sarıkent', 90 | 'Sarımsaklı', 'Sarıoğlan', 'Savaştepe', 'Sağlık', 'Sekili', 'Selçuk', 91 | 'Sevindik', 'Seyitler', 'Sincan', 'Sindirler', 'Sinekli', 'Sivas', 92 | 'Sivas(Adatepe)', 'Sivrice', 'Soma', 'Sorgun YHT', 'Soğucak', 'Subaşı', 93 | 'Sudurağı', 'Sultandağı', 'Sultanhisar', 'Suluova', 'Susurluk', 94 | 'Suveren', 'Suçatı', 'Söke', 'Söğütlü Durak', 'Süngütaşı', 'Sünnetçiler', 95 | 'Sütlaç', 'Sıcaksu', 'Tanyeri', 'Tatvan Gar', 'Tavşanlı', 'Tayyakadın', 96 | 'Taşkent', 'Tecer', 'Tepeköy', 'Tokat(Yeşilyurt)', 'Topaç', 'Topdağı', 97 | 'Topulyurdu', 'Torbalı', 'Turgutlu', 'Turhal', 'Tuzhisar', 'Tüney', 98 | 'Türkoğlu', 'Tınaztepe', 'Ulam', 'Uluköy', 'Ulukışla', 'Uluova', 99 | 'Umurlu', 'Urganlı', 'Uyanık', 'Uzunköprü', 'Uşak', 'Velimeşe', 'Vezirhan', 100 | 'Yahşihan', 'Yahşiler', 'Yakapınar', 'Yarbaşı', 'Yarımca YHT', 'Yayla', 101 | 'Yaylıca', 'Yazlak', 'Yazıhan', 'Yağdonduran', 'Yeni Karasar', 'Yenice', 102 | 'Yenice D', 'Yenifakılı', 'Yenikangal', 'Yeniköy', 'Yeniçubuk', 'Yerköy', 103 | 'Yeşilhisar', 'Yolçatı', 'Yozgat YHT', 'Yunusemre', 'Yurt', 'Yıldırımkemal', 104 | 'Yıldızeli', 'Zile', 'Çadırkaya', 'Çakmak', 'Çalıköy', 'Çamlık', 'Çankırı', 105 | 'Çardak', 'Çatalca', 'Çavundur', 'Çavuşcugöl', 'Çay', 'Çağlar', 'Çelikalan', 106 | 'Çerikli', 'Çerkezköy', 'Çerkeş', 'Çetinkaya', 'Çiftehan', 'Çiftlik', 107 | 'Çizözü', 'Çiğli', 'Çobanhasan', 'Çorlu', 'Çukurbük', 'Çukurhüseyin', 108 | 'Çumra', 'Çöltepe', 'Çöğürler', 'İhsaniye', 'İliç', 'İnay', 'İncirlik', 109 | 'İncirliova', 'İsabeyli', 'İshakçelebi', 'İsmetpaşa', 'İstanbul(Bakırköy)', 110 | 'İstanbul(Bostancı)', 'İstanbul(Halkalı)', 'İstanbul(Pendik)', 'İstanbul(Söğütlüçeşme)', 111 | 'İzmir (Basmane)', 'İzmit YHT', 'Şakirpaşa', 'Şarkışla', 'Şefaatli', 112 | 'Şefkat', 'Şehitlik', 'Şerbettar' 113 | ]; 114 | 115 | 116 | 117 | Future searchTickets() async { 118 | if (from == null || to == null) { 119 | ScaffoldMessenger.of(context).showSnackBar( 120 | SnackBar(content: Text('Lütfen nereden ve nereye istasyonlarını seçin.')), 121 | ); 122 | return; 123 | } 124 | final response = await http.get(Uri.parse( 125 | 'https://tcdd-api.vercel.app/search?nereden=$from&nereye=$to&tarih=${selectedDate.toIso8601String().split('T')[0]}')); 126 | 127 | if (response.statusCode == 200) { 128 | setState(() { 129 | tickets = json.decode(response.body); 130 | }); 131 | } else { 132 | ScaffoldMessenger.of(context).showSnackBar( 133 | SnackBar(content: Text('Bilet bulunamadı')), 134 | ); 135 | } 136 | } 137 | 138 | @override 139 | Widget build(BuildContext context) { 140 | return Scaffold( 141 | body: SafeArea( 142 | child: Padding( 143 | padding: const EdgeInsets.all(16.0), 144 | child: Column( 145 | crossAxisAlignment: CrossAxisAlignment.start, 146 | children: [ 147 | Row( 148 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 149 | children: [ 150 | Text( 151 | "Nereye Gitmek İstersiniz?", 152 | style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white), 153 | ), 154 | Icon(Icons.train_outlined, color: Colors.white), 155 | ], 156 | ), 157 | SizedBox(height: 20), 158 | _buildDropdownField('Nereden', from, (String? newValue) { 159 | setState(() { 160 | from = newValue; 161 | }); 162 | }), 163 | _buildDropdownField('Nereye', to, (String? newValue) { 164 | setState(() { 165 | to = newValue; 166 | }); 167 | }), 168 | _buildDateField(), 169 | SizedBox(height: 20), 170 | Center( 171 | child: GestureDetector( 172 | onTap: searchTickets, 173 | child: Row( 174 | mainAxisAlignment: MainAxisAlignment.center, 175 | children: [ 176 | _buildTabButton('Bileti hemen bul', Icons.train, true), 177 | SizedBox(width: 30), 178 | ], 179 | ), 180 | ), 181 | ), 182 | 183 | SizedBox(height: 20), 184 | Expanded( 185 | child: ListView.builder( 186 | itemCount: tickets.length, 187 | itemBuilder: (context, index) { 188 | final ticket = tickets[index]; 189 | return GestureDetector( 190 | onTap: () { 191 | Navigator.push( 192 | context, 193 | MaterialPageRoute( 194 | builder: (context) => TicketDetailScreen(ticket: ticket), 195 | ), 196 | ); 197 | }, 198 | child: Card( 199 | color: Colors.grey[800], 200 | child: ListTile( 201 | title: Text(ticket['trenAdi'], style: TextStyle(color: Colors.white)), 202 | subtitle: Text('${ticket['binisTarih']} - ${ticket['inisTarih']}', style: TextStyle(color: Colors.grey)), 203 | ), 204 | ).animate( 205 | 206 | effects: [ 207 | FadeEffect(duration: 300.ms), 208 | SlideEffect( 209 | begin: Offset(0.3, 0), // Dikey kaydırma 210 | end: Offset.zero, 211 | duration: 300.ms, 212 | ), 213 | ], 214 | ), 215 | ); 216 | }, 217 | ), 218 | ) 219 | 220 | ], 221 | ), 222 | ), 223 | ), 224 | bottomNavigationBar: BottomNavigationBar( 225 | backgroundColor: Color(0xFF1C2331), 226 | selectedItemColor: Color(0xFFFFA500), 227 | unselectedItemColor: Colors.grey, 228 | items: [ 229 | BottomNavigationBarItem(icon: Icon(Icons.home), label: ''), 230 | BottomNavigationBarItem(icon: Icon(Icons.list), label: ''), 231 | ], 232 | ), 233 | ); 234 | } 235 | 236 | Widget _buildTabButton(String text, IconData icon, bool isSelected) { 237 | return Container( 238 | padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), 239 | decoration: BoxDecoration( 240 | color: isSelected ? Color(0xFFFFA500) : Colors.grey[800], 241 | borderRadius: BorderRadius.circular(20), 242 | ), 243 | child: Row( 244 | children: [ 245 | Icon(icon, color: isSelected ? Colors.white : Colors.grey), 246 | SizedBox(width: 5), 247 | Text(text, style: TextStyle(color: isSelected ? Colors.white : Colors.grey)), 248 | ], 249 | ), 250 | ); 251 | } 252 | 253 | Widget _buildDropdownField(String label, String? value, ValueChanged onChanged) { 254 | return Padding( 255 | padding: const EdgeInsets.symmetric(vertical: 8.0), 256 | child: DropdownButtonFormField( 257 | value: value, 258 | decoration: InputDecoration( 259 | labelText: label, 260 | labelStyle: TextStyle(color: Colors.grey), 261 | suffixIcon: Icon(Icons.train, color: Colors.grey), 262 | enabledBorder: UnderlineInputBorder( 263 | borderSide: BorderSide(color: Colors.grey), 264 | ), 265 | focusedBorder: UnderlineInputBorder( 266 | borderSide: BorderSide(color: Color(0xFFFFA500)), 267 | ), 268 | ), 269 | style: TextStyle(color: Colors.white), 270 | onChanged: onChanged, 271 | items: stations.map((String station) { 272 | return DropdownMenuItem( 273 | value: station, 274 | child: Text(station), 275 | ); 276 | }).toList(), 277 | ), 278 | ); 279 | } 280 | 281 | Widget _buildDateField() { 282 | return Padding( 283 | padding: const EdgeInsets.symmetric(vertical: 8.0), 284 | child: TextField( 285 | decoration: InputDecoration( 286 | labelText: 'Tarih seç', 287 | labelStyle: TextStyle(color: Colors.grey), 288 | suffixIcon: Icon(Icons.calendar_today, color: Colors.grey), 289 | enabledBorder: UnderlineInputBorder( 290 | borderSide: BorderSide(color: Colors.grey), 291 | ), 292 | focusedBorder: UnderlineInputBorder( 293 | borderSide: BorderSide(color: Color(0xFFFFA500)), 294 | ), 295 | ), 296 | style: TextStyle(color: Colors.white), 297 | controller: TextEditingController(text: selectedDate.toLocal().toString().split(' ')[0]), 298 | readOnly: true, 299 | onTap: () async { 300 | final DateTime? picked = await showDatePicker( 301 | context: context, 302 | initialDate: selectedDate, 303 | firstDate: DateTime.now(), 304 | lastDate: DateTime(2025), 305 | ); 306 | if (picked != null && picked != selectedDate) { 307 | setState(() { 308 | selectedDate = picked; 309 | }); 310 | } 311 | }, 312 | ), 313 | ); 314 | } 315 | } 316 | 317 | class TicketDetailScreen extends StatelessWidget { 318 | final dynamic ticket; 319 | 320 | TicketDetailScreen({required this.ticket}); 321 | 322 | @override 323 | Widget build(BuildContext context) { 324 | return Scaffold( 325 | backgroundColor: Color(0xFF1C2331), 326 | appBar: AppBar( 327 | title: Text('Bilet Detayı'), 328 | backgroundColor: Color(0xFF1C2331), 329 | ), 330 | body: SingleChildScrollView( 331 | child: Column( 332 | children: [ 333 | Container( 334 | height: 710, 335 | child: ListView.builder( 336 | scrollDirection: Axis.horizontal, 337 | itemCount: ticket['vagonTipleri'].length, 338 | itemBuilder: (context, index) { 339 | final vagon = ticket['vagonTipleri'][index]; 340 | return Padding( 341 | padding: const EdgeInsets.all(10.0), 342 | child: TicketWidget( 343 | width: 350, 344 | height: 500, 345 | isCornerRounded: true, 346 | padding: EdgeInsets.all(40), 347 | child: TicketData(ticket: ticket, vagon: vagon), 348 | ), 349 | ); 350 | }, 351 | ), 352 | ), 353 | ], 354 | ), 355 | ), 356 | ); 357 | } 358 | } 359 | 360 | class TicketData extends StatelessWidget { 361 | final dynamic ticket; 362 | final dynamic vagon; 363 | 364 | const TicketData({ 365 | Key? key, 366 | required this.ticket, 367 | required this.vagon, 368 | }) : super(key: key); 369 | 370 | @override 371 | Widget build(BuildContext context) { 372 | final binisTarihSaat = ticket['binisTarih'].split(' '); 373 | final inisTarihSaat = ticket['inisTarih'].split(' '); 374 | 375 | return SingleChildScrollView( 376 | child: Column( 377 | crossAxisAlignment: CrossAxisAlignment.start, 378 | mainAxisSize: MainAxisSize.min, 379 | children: [ 380 | Row( 381 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 382 | children: [ 383 | Container( 384 | width: 120.0, 385 | height: 30.0, 386 | decoration: BoxDecoration( 387 | borderRadius: BorderRadius.circular(30.0), 388 | border: Border.all(width: 1.0, color: Colors.green), 389 | ), 390 | child: Center( 391 | child: Text( 392 | '${ticket['seferId'].toString()}', 393 | style: TextStyle(color: Colors.green, fontSize: 12), 394 | ), 395 | ), 396 | ), 397 | Image.network( 398 | 'https://telegra.ph/file/c6a883fa4c3fbfe7fc849.png', 399 | width: 100, 400 | height: 80, 401 | fit: BoxFit.contain, 402 | ), 403 | ], 404 | ), 405 | SizedBox(height: 10), 406 | Text( 407 | 'Sefer Seçimi', 408 | style: TextStyle(color: Colors.black, fontSize: 18.0, fontWeight: FontWeight.bold), 409 | ), 410 | SizedBox(height: 15), 411 | ticketDetailsWidget('Tren Adı', ticket['trenAdi'], 'Tarih', binisTarihSaat[0]+ ' ' + binisTarihSaat[1]+ ' ' + binisTarihSaat[2]), 412 | ticketDetailsWidget('Kalkış', binisTarihSaat[3] + ' ' + binisTarihSaat[4], 'Varış', inisTarihSaat[3]+ ' ' + inisTarihSaat[4]), 413 | ticketDetailsWidget('Tren Tipi', ticket['trenTipi'], 'Gün Notu', ticket['gunNotu']), 414 | ticketDetailsWidget('Vagon Tipi', vagon['vagonTip'], 'Kalan Koltuk', '${vagon['kalanSayi']}'), 415 | ticketDetailsWidget('Ücret', '${vagon['toplamUcret']} TL', 'Kalan Yatak', '${vagon['kalanYatakSayisi']}'), 416 | SizedBox(height: 20), 417 | Center( 418 | child: Container( 419 | width: 130, 420 | height: 130, 421 | decoration: BoxDecoration( 422 | image: DecorationImage( 423 | image: NetworkImage('https://quickchart.io/qr?text=ebilet.tcddtasimacilik.gov.tr&size=200'), 424 | fit: BoxFit.cover, 425 | ), 426 | ), 427 | ), 428 | ), 429 | SizedBox(height: 10), 430 | Center( 431 | child: Text( 432 | '@codermert | UPO Software Technologies', 433 | style: TextStyle(color: Colors.black, fontSize: 12), 434 | ), 435 | ), 436 | ], 437 | ), 438 | ); 439 | } 440 | } 441 | 442 | Widget ticketDetailsWidget(String firstTitle, String firstDesc, String secondTitle, String secondDesc) { 443 | return Padding( 444 | padding: const EdgeInsets.symmetric(vertical: 5.0), 445 | child: Row( 446 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 447 | children: [ 448 | Expanded( 449 | child: Column( 450 | crossAxisAlignment: CrossAxisAlignment.start, 451 | children: [ 452 | Text(firstTitle, style: TextStyle(color: Colors.grey, fontSize: 12)), 453 | Text(firstDesc, style: TextStyle(color: Colors.black, fontSize: 14)), 454 | ], 455 | ), 456 | ), 457 | Expanded( 458 | child: Column( 459 | crossAxisAlignment: CrossAxisAlignment.start, 460 | children: [ 461 | Text(secondTitle, style: TextStyle(color: Colors.grey, fontSize: 12)), 462 | Text(secondDesc, style: TextStyle(color: Colors.black, fontSize: 14)), 463 | ], 464 | ), 465 | ) 466 | ], 467 | ), 468 | ); 469 | } -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tcdd 2 | description: "A new Flutter project." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: '>=3.4.3 <4.0.0' 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | 35 | # The following adds the Cupertino Icons font to your application. 36 | # Use with the CupertinoIcons class for iOS style icons. 37 | cupertino_icons: ^1.0.6 38 | ticket_widget: ^1.0.2 39 | flutter_animate: ^4.5.0 40 | http: ^1.2.2 41 | google_fonts: ^6.2.1 42 | 43 | dev_dependencies: 44 | flutter_test: 45 | sdk: flutter 46 | 47 | # The "flutter_lints" package below contains a set of recommended lints to 48 | # encourage good coding practices. The lint set provided by the package is 49 | # activated in the `analysis_options.yaml` file located at the root of your 50 | # package. See that file for information about deactivating specific lint 51 | # rules and activating additional ones. 52 | flutter_lints: ^3.0.0 53 | 54 | # For information on the generic Dart part of this file, see the 55 | # following page: https://dart.dev/tools/pub/pubspec 56 | 57 | # The following section is specific to Flutter packages. 58 | flutter: 59 | 60 | # The following line ensures that the Material Icons font is 61 | # included with your application, so that you can use the icons in 62 | # the material Icons class. 63 | uses-material-design: true 64 | 65 | # To add assets to your application, add an assets section, like this: 66 | # assets: 67 | # - images/a_dot_burr.jpeg 68 | # - images/a_dot_ham.jpeg 69 | 70 | # An image asset can refer to one or more resolution-specific "variants", see 71 | # https://flutter.dev/assets-and-images/#resolution-aware 72 | 73 | # For details regarding adding assets from package dependencies, see 74 | # https://flutter.dev/assets-and-images/#from-packages 75 | 76 | # To add custom fonts to your application, add a fonts section here, 77 | # in this "flutter" section. Each entry in this list should have a 78 | # "family" key with the font family name, and a "fonts" key with a 79 | # list giving the asset and other descriptors for the font. For 80 | # example: 81 | # fonts: 82 | # - family: Schyler 83 | # fonts: 84 | # - asset: fonts/Schyler-Regular.ttf 85 | # - asset: fonts/Schyler-Italic.ttf 86 | # style: italic 87 | # - family: Trajan Pro 88 | # fonts: 89 | # - asset: fonts/TrajanPro.ttf 90 | # - asset: fonts/TrajanPro_Bold.ttf 91 | # weight: 700 92 | # 93 | # For details regarding fonts from package dependencies, 94 | # see https://flutter.dev/custom-fonts/#from-packages 95 | --------------------------------------------------------------------------------