Created
September 14, 2021 10:39
-
-
Save doyle-flutter/6d9ebca8a974374bc1d82d26e792db5d to your computer and use it in GitHub Desktop.
good & bed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| void main() => runApp(Sys()); | |
| class Sys extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) => MaterialApp( | |
| onGenerateRoute: (RouteSettings route){ | |
| // if(route.name == Main.path) return MaterialPageRoute( | |
| // settings: RouteSettings(name: '/'), | |
| // builder: (context) => Main(controller: MainController(),) | |
| // ); | |
| return MaterialPageRoute( | |
| settings: RouteSettings(name: '/'), | |
| builder: (context) => Main(controller: MainController(),) | |
| ); | |
| }, | |
| ); | |
| } | |
| class MainController{} | |
| class Main extends StatelessWidget { | |
| final MainController controller; | |
| const Main({Key? key, required this.controller}) : super(key: key); | |
| static const String path = "/"; | |
| @override | |
| Widget build(BuildContext context) => MainPage(controller: this.controller,); | |
| } | |
| class MainPage extends StatelessWidget { | |
| final MainController controller; | |
| const MainPage({Key? key, required this.controller}) : super(key: key); | |
| @override | |
| Widget build(BuildContext context) => this._wrapper( | |
| context: context, | |
| children: [ | |
| ProfileComponent( | |
| imgSrc: "https://cdn.pixabay.com/photo/2016/03/31/19/58/avatar-1295429__480.png", | |
| clientName: "홍길동", | |
| ), | |
| ItemsViewComponent( | |
| itemMenus: <String>["menu1","menu2","menu3"].map<TextButton>( | |
| (String s) => TextButton( | |
| child: Text( | |
| s, | |
| style:TextStyle( | |
| color: Colors.white, | |
| fontWeight: FontWeight.bold | |
| ) | |
| ), | |
| onPressed: (){}, | |
| ) | |
| ).toList(), | |
| itemOnPressed: (int itemIndex) => print("itemIndex : $itemIndex"), | |
| itemIconOnPressed: (int itemIconIndex) => print("Icon : $itemIconIndex"), | |
| items: <String>[ | |
| "https://cdn.pixabay.com/photo/2020/07/21/16/24/landscape-5426755__480.jpg", | |
| "https://cdn.pixabay.com/photo/2021/09/10/22/28/smart-working-6614315__340.png", | |
| "https://cdn.pixabay.com/photo/2021/09/09/07/54/woman-6609797__480.png", | |
| "https://cdn.pixabay.com/photo/2021/08/23/17/53/cat-6568422__480.jpg", | |
| ].map<ItemsViewModel>((String img) => ItemsViewModel(imgSrc: img)).toList(), | |
| height: MediaQuery.of(context).size.height*0.50, | |
| ), | |
| ListItemComponent( | |
| items: [ | |
| SalesModel( | |
| imgSrc: "https://cdn.pixabay.com/photo/2021/09/06/00/19/eggs-6600556__480.png", | |
| title: "Product1", | |
| shopName: "shop111" | |
| ), | |
| SalesModel( | |
| imgSrc: "https://cdn.pixabay.com/photo/2021/09/06/00/22/wheat-flour-6600578__480.png", | |
| title: "Product2", | |
| shopName: "shop22222" | |
| ), | |
| SalesModel( | |
| imgSrc: "https://cdn.pixabay.com/photo/2021/08/28/19/49/donut-6581822__480.png", | |
| title: "Product3", | |
| shopName: "shop3333" | |
| ), | |
| SalesModel( | |
| imgSrc: "https://cdn.pixabay.com/photo/2021/01/15/17/01/green-5919790__480.jpg", | |
| title: "Product4", | |
| shopName: "shop4" | |
| ) | |
| ], | |
| ) | |
| ] | |
| ); | |
| Widget _wrapper({required BuildContext context, required List<Widget> children}) => Scaffold( | |
| appBar: AppBar( | |
| backgroundColor: Colors.white, | |
| elevation: 0, | |
| title: Text("🧑💻 JamesDev", style: TextStyle(color: Colors.black)), | |
| centerTitle: true | |
| ), | |
| backgroundColor: Colors.white, | |
| body: SingleChildScrollView( | |
| child: Container( | |
| width: MediaQuery.of(context).size.width, | |
| padding: EdgeInsets.only(bottom: 40.0), | |
| color: Colors.grey[200], | |
| child: Column( | |
| children: children, | |
| ), | |
| ), | |
| ), | |
| bottomNavigationBar: BottomNavigationBar( | |
| showSelectedLabels: false, | |
| showUnselectedLabels: false, | |
| selectedItemColor: Colors.deepOrangeAccent, | |
| type: BottomNavigationBarType.fixed, | |
| items: [ | |
| BottomNavigationBarItem(icon: Icon(Icons.home), label: ""), | |
| BottomNavigationBarItem(icon: Icon(Icons.note_sharp), label: ""), | |
| BottomNavigationBarItem(icon: Icon(Icons.music_note), label: ""), | |
| BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), label: ""), | |
| BottomNavigationBarItem(icon: Icon(Icons.person), label: "") | |
| ], | |
| ), | |
| ); | |
| } | |
| class ProfileComponent extends StatelessWidget { | |
| final double? width; | |
| final double? height; | |
| final String imgSrc; | |
| final String clientName; | |
| final EdgeInsets? margin; | |
| final EdgeInsets? padding; | |
| TextStyle? titleTextStyle; | |
| ProfileComponent({ | |
| Key? key, | |
| this.width, | |
| this.height, | |
| required this.imgSrc, | |
| required this.clientName, | |
| this.margin, | |
| this.padding, | |
| this.titleTextStyle, | |
| }) : super(key: key); | |
| final TextStyle _defaultTitleTextStyle = TextStyle( | |
| fontWeight: FontWeight.bold, | |
| fontSize: 20.0, | |
| ); | |
| @override | |
| Widget build(BuildContext context) => Card( | |
| margin: this.margin ?? EdgeInsets.only(bottom: 30.0, left: 5.0, right: 5.0), | |
| child: Container( | |
| width: this.width, | |
| height: this.height, | |
| padding: this.padding ?? EdgeInsets.all(20.0), | |
| child: Column( | |
| children: [ | |
| Container( | |
| child: Row( | |
| mainAxisAlignment: MainAxisAlignment.spaceAround, | |
| children: [ | |
| Container( | |
| child: Column( | |
| crossAxisAlignment: CrossAxisAlignment.start, | |
| children: [ | |
| Container( | |
| child: Text( | |
| "안녕하세요, ${this.clientName}님?!", | |
| style: this.titleTextStyle ?? this._defaultTitleTextStyle, | |
| ), | |
| ), | |
| Container( | |
| child: Text("오늘도 지구를 지켜츄세요 ❤️"), | |
| ) | |
| ], | |
| ) | |
| ), | |
| Container( | |
| child: CircleAvatar( | |
| backgroundImage: NetworkImage(this.imgSrc), | |
| minRadius: 40.0, | |
| backgroundColor: Colors.white, | |
| child: Container( | |
| width: 80.0, | |
| height: 80.0, | |
| decoration: BoxDecoration( | |
| borderRadius: BorderRadius.circular(80.0), | |
| border: Border.all(color: Colors.grey) | |
| ) | |
| ), | |
| ) | |
| ) | |
| ], | |
| ), | |
| ), | |
| Container( | |
| margin: EdgeInsets.only(top:20.0), | |
| child: TextField( | |
| readOnly: true, | |
| decoration: InputDecoration( | |
| filled: true, | |
| fillColor: Colors.amberAccent, | |
| suffixIcon: Container( | |
| padding: EdgeInsets.all(10.0), | |
| child: Icon( | |
| Icons.search, | |
| color: Colors.white | |
| ) | |
| ), | |
| contentPadding: EdgeInsets.all(10.0), | |
| hintText: "Search...", | |
| hintStyle: TextStyle( | |
| color: Colors.white, | |
| fontWeight: FontWeight.bold, | |
| fontSize: 20.0 | |
| ), | |
| border: OutlineInputBorder( | |
| borderRadius: BorderRadius.circular(10.0), | |
| borderSide: BorderSide.none | |
| ) | |
| ), | |
| ), | |
| ) | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| class ItemsViewModel{ | |
| final String imgSrc; | |
| const ItemsViewModel({required this.imgSrc}); | |
| } | |
| /// 좋은 예시 | |
| class ItemsViewComponent extends StatefulWidget { | |
| late Axis scrollAxis; | |
| final List<ItemsViewModel> items; | |
| final EdgeInsets? margin; | |
| final EdgeInsets? padding; | |
| final double? width; | |
| final double height; | |
| final void Function(int) itemOnPressed; | |
| final void Function(int) itemIconOnPressed; | |
| final List<TextButton> itemMenus; | |
| ItemsViewComponent({ | |
| Key? key, | |
| this.width, | |
| required this.height, | |
| Axis scrollAxis = Axis.horizontal, | |
| required this.items, | |
| required this.itemOnPressed, | |
| required this.itemIconOnPressed, | |
| required this.itemMenus, | |
| this.margin, | |
| this.padding | |
| }) : super(key: key){ | |
| this.scrollAxis = scrollAxis; | |
| } | |
| @override | |
| State<ItemsViewComponent> createState() => _ItemsViewComponentState(); | |
| } | |
| class _ItemsViewComponentState extends State<ItemsViewComponent> { | |
| final PageController _controller = PageController( | |
| viewportFraction: 0.8, | |
| keepPage: false | |
| ); | |
| int _pageIndex = 0; | |
| @override | |
| void initState() { | |
| this._controller.addListener(() { | |
| if(!this._controller.hasClients) return; | |
| if(!this.mounted) return; | |
| setState((){}); | |
| }); | |
| super.initState(); | |
| } | |
| @override | |
| void dispose() { | |
| this._controller.dispose(); | |
| super.dispose(); | |
| } | |
| @override | |
| Widget build(BuildContext context) => Card( | |
| margin: this.widget.margin ?? EdgeInsets.only(bottom: 30.0, left: 5.0, right: 5.0), | |
| child: Container( | |
| color: Colors.white, | |
| width: this.widget.width, | |
| height: this.widget.height, | |
| padding: this.widget.padding ?? EdgeInsets.symmetric(vertical: 14.0), | |
| child: PageView.builder( | |
| scrollDirection: this.widget.scrollAxis, | |
| controller: this._controller, | |
| padEnds: false, | |
| itemCount: this.widget.items.length, | |
| onPageChanged: (int currentPageIndex){ | |
| setState(() { | |
| this._pageIndex = currentPageIndex; | |
| }); | |
| }, | |
| itemBuilder: (BuildContext context, int index){ | |
| return AnimatedBuilder( | |
| animation: this._controller, | |
| builder: (BuildContext context, Widget? child) { | |
| double value = 1.0; | |
| if (_controller.position.haveDimensions) { | |
| value = _controller.page! - index; | |
| value = (1 - (value.abs() * .3)).clamp(0.0, 1.0); | |
| } | |
| return Container( | |
| margin: EdgeInsets.symmetric(horizontal: 10.0), | |
| child: Center( | |
| child: SizedBox( | |
| height: Curves.easeOut.transform(value) * widget.height, | |
| width: Curves.easeOut.transform(value) * (MediaQuery.of(context).size.width), | |
| child: child, | |
| ), | |
| ), | |
| ); | |
| }, | |
| child: Opacity( | |
| opacity: this._pageIndex == index ? 1.0 : 0.3, | |
| child: index == 0 ? this._firstItemWrapper(this._item(widget.items[index], index)) : this._item(widget.items[index], index) | |
| ), | |
| ); | |
| }, | |
| ), | |
| ), | |
| ); | |
| Widget _firstItemWrapper(Widget content) => Container( | |
| child: Stack( | |
| children: [ | |
| content, | |
| Positioned( | |
| left: 0, | |
| top: 0, | |
| bottom: 0, | |
| child: Container( | |
| padding: EdgeInsets.only(left: 20.0), | |
| child: Column( | |
| children: this.widget.itemMenus.map<Widget>( | |
| (TextButton s) => Expanded( | |
| child: Container( | |
| alignment: Alignment.center, | |
| child: RotatedBox( | |
| quarterTurns: -1, | |
| child: s, | |
| ), | |
| ) | |
| ) | |
| ).toList(), | |
| ) | |
| ), | |
| ) | |
| ], | |
| ) | |
| ); | |
| Widget _item(ItemsViewModel model, int index) => Container( | |
| width: double.infinity, | |
| decoration: BoxDecoration( | |
| image: DecorationImage( | |
| fit: BoxFit.cover, | |
| image: NetworkImage( | |
| model.imgSrc | |
| ), | |
| ), | |
| borderRadius: BorderRadius.circular(10.0) | |
| ), | |
| child: Column( | |
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
| children: [ | |
| Container( | |
| padding: EdgeInsets.all(10.0), | |
| alignment: Alignment.centerRight, | |
| child: IconButton( | |
| icon: Icon( | |
| Icons.more_horiz, | |
| color: Colors.white | |
| ), | |
| onPressed: () => this.widget.itemIconOnPressed(index), | |
| ) | |
| ), | |
| Container( | |
| padding: EdgeInsets.only(bottom: 10.0), | |
| child: MaterialButton( | |
| child: Text( | |
| "Click", | |
| style: TextStyle( | |
| fontWeight: FontWeight.bold | |
| ), | |
| ), | |
| onPressed: () => this.widget.itemOnPressed(index), | |
| shape: RoundedRectangleBorder( | |
| borderRadius: BorderRadius.circular(10.0), | |
| ), | |
| minWidth: 200.0, | |
| color: Colors.orangeAccent, | |
| textColor: Colors.white | |
| ) | |
| ) | |
| ], | |
| ), | |
| ); | |
| } | |
| abstract class ItemModel{ | |
| final String imgSrc = ""; | |
| final String title = ""; | |
| } | |
| class SalesModel implements ItemModel{ | |
| @override | |
| final String imgSrc; | |
| @override | |
| final String title; | |
| final String shopName; | |
| const SalesModel({required this.imgSrc, required this.title, required this.shopName}); | |
| } | |
| /// 나쁜 예시 | |
| class ListItemComponent extends StatelessWidget { | |
| final EdgeInsets? margin; | |
| final EdgeInsets? padding; | |
| final List<ItemModel> items; | |
| const ListItemComponent({ | |
| Key? key, | |
| required this.items, | |
| this.margin, | |
| this.padding | |
| }) : super(key: key); | |
| @override | |
| Widget build(BuildContext context) => Card( | |
| margin: this.margin ?? EdgeInsets.only(bottom: 30.0, left: 5.0, right: 5.0), | |
| child: Container( | |
| padding: this.padding ?? EdgeInsets.only(bottom: 20.0), | |
| color: Colors.white, | |
| child: Column( | |
| children: [ | |
| Container( | |
| padding: EdgeInsets.only( | |
| top: 30.0, | |
| left: 20.0, | |
| bottom: 10.0 | |
| ), | |
| alignment: Alignment.centerLeft, | |
| child: Text( | |
| 'TITLE', | |
| style: TextStyle( | |
| fontSize: 20.0, | |
| fontWeight: FontWeight.bold | |
| ) | |
| ), | |
| ), | |
| Container( | |
| child: Row( | |
| children: <String>["menu1", "menu2", "menu3"].map<Widget>( | |
| (String m) => Expanded( | |
| child: Container( | |
| padding: EdgeInsets.symmetric( | |
| vertical: 4.0, | |
| horizontal: 10.0 | |
| ), | |
| child: MaterialButton( | |
| color: m == "menu1" ? Colors.orange : Colors.grey, | |
| textColor: Colors.white, | |
| shape: RoundedRectangleBorder( | |
| borderRadius: BorderRadius.circular(20.0), | |
| side: BorderSide(color: m == "menu1" ? Colors.transparent : Colors.grey) | |
| ), | |
| child: Text(m), | |
| onPressed: (){} | |
| ), | |
| ), | |
| ) | |
| ).toList(), | |
| ), | |
| ), | |
| Container( | |
| child: Column( | |
| children: this.items.map<Widget>( | |
| (ItemModel model) => Material( | |
| color: Colors.white, | |
| child: InkWell( | |
| onTap: (){}, | |
| child: Container( | |
| padding: EdgeInsets.symmetric(horizontal: 30.0, vertical: 20.0), | |
| child: Row( | |
| children: [ | |
| Container( | |
| width: 60.0, | |
| height: 60.0, | |
| padding: EdgeInsets.all(10.0), | |
| decoration: BoxDecoration( | |
| image: DecorationImage( | |
| fit: BoxFit.cover, | |
| image: NetworkImage(model.imgSrc) | |
| ), | |
| borderRadius: BorderRadius.circular(20.0), | |
| border: Border.all(color: Colors.grey) | |
| ), | |
| ), | |
| SizedBox(width: 30.0), | |
| Container( | |
| child: Column( | |
| crossAxisAlignment: CrossAxisAlignment.start, | |
| children: [ | |
| Text( | |
| model.title, | |
| style: TextStyle( | |
| fontWeight: FontWeight.bold, | |
| fontSize: 16.0, | |
| ), | |
| ), | |
| Text( | |
| (model as SalesModel).shopName, | |
| style: TextStyle( | |
| color: Colors.deepOrangeAccent | |
| ), | |
| ), | |
| ], | |
| ) | |
| ), | |
| ], | |
| ), | |
| ), | |
| ), | |
| ) | |
| ).toList(), | |
| ), | |
| ), | |
| ], | |
| ) | |
| ), | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment