diff --git a/lib/main.dart b/lib/main.dart index 05bb902..8ca587e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); @@ -55,71 +56,127 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } + final Color _color = Colors.blueAccent; @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + body: const MyWidget(), + ); + } +} + +class MyWidget extends StatelessWidget { + const MyWidget({super.key}); + + @override + Widget build(BuildContext context) { + final data = [ + _CardData( + 'Зов Ктулху', + descriptionText: 'Лучше бы не находить разгадку, объединяющую сошедших с ума творцов...', + imageUrl: 'https://lovecraft.country/images/bibliography/call-of-cthulhu-mini.webp' + ), + _CardData( + 'Хребты безумия', + descriptionText: '«Хребты безумия» написаны в документальной манере повествования, постепенно привыкая к которой, становишься свидетелем особой реальности описываемых событий.', + icon: Icons.hail, + imageUrl: 'https://lovecraft.country/images/bibliography/At-the-Mountains-of-Madness-mini.webp' + ), + _CardData('Тень над Инсмутом', descriptionText: 'Инсмут, маленький рыбацкий городок неподалеку от Аркхэма, уже много лет имеет дурную славу. В округе ходят жуткие истории о его угрюмых и уродливых жителях, от которых лучше держаться подальше.', icon: Icons.warning_amber, imageUrl: 'https://lovecraft.country/images/bibliography/The-Shadow-over-Innsmouth.webp') + ]; + return Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: data.map((e) => _Card.fromData(e)).toList(), + ), + ), + ); + } +} + +class _CardData { + final String text; + final String descriptionText; + final IconData icon; + final String? imageUrl; + + _CardData(this.text, + {required this.descriptionText, + this.icon = Icons.ac_unit_outlined, + this.imageUrl}); +} + +class _Card extends StatelessWidget { + final String text; + final String descriptionText; + final IconData icon; + final String? imageUrl; + + const _Card(this.text, + {required this.descriptionText, + this.icon = Icons.ac_unit_outlined, + this.imageUrl}); + + factory _Card.fromData(_CardData data) => _Card( + data.text, + descriptionText: data.descriptionText, + icon: data.icon, + imageUrl: data.imageUrl, + ); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(top: 16), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.amber, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.grey, + width: 2, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: SizedBox( + width: 140, + height: 100, + child: Image.network( + imageUrl ?? '', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Placeholder(), + ), + ), + ), + Flexible( + child: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(text, style: Theme.of(context).textTheme.headlineLarge), + Text(descriptionText, + style: Theme.of(context).textTheme.bodyLarge), + ], + ), + )), + Padding( + padding: const EdgeInsets.only(left:8.0), + child: Icon(icon), + ), + ], + ), ); } }