diff --git a/lib/components/blog.dart b/lib/components/blog.dart index 8b3da6b..2ae6362 100644 --- a/lib/components/blog.dart +++ b/lib/components/blog.dart @@ -1,16 +1,22 @@ +import 'dart:js'; + import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:minimal/components/color.dart'; import 'package:minimal/components/spacing.dart'; import 'package:minimal/components/text.dart'; import 'package:minimal/components/typography.dart'; +import 'package:minimal/models/header_model.dart'; import 'package:minimal/routes.dart'; +import 'package:responsive_framework/responsive_framework.dart'; -class ImageWrapper extends StatelessWidget { - final String image; +import '../utils/globals.dart'; +class ImageWrapper extends StatelessWidget { const ImageWrapper({Key? key, required this.image}) : super(key: key); + final String image; + @override Widget build(BuildContext context) { //TODO Listen to inherited widget width updates. @@ -28,10 +34,10 @@ class ImageWrapper extends StatelessWidget { } class TagWrapper extends StatelessWidget { - final List tags; - const TagWrapper({Key? key, this.tags = const []}) : super(key: key); + final List tags; + @override Widget build(BuildContext context) { return Container( @@ -45,10 +51,10 @@ class TagWrapper extends StatelessWidget { } class Tag extends StatelessWidget { - final String tag; - const Tag({Key? key, required this.tag}) : super(key: key); + final String tag; + @override Widget build(BuildContext context) { return RawMaterialButton( @@ -69,10 +75,10 @@ class Tag extends StatelessWidget { } class ReadMoreButton extends StatelessWidget { - final VoidCallback onPressed; - const ReadMoreButton({Key? key, required this.onPressed}) : super(key: key); + final VoidCallback onPressed; + @override Widget build(BuildContext context) { return OutlinedButton( @@ -103,7 +109,10 @@ class ReadMoreButton extends StatelessWidget { states.contains(MaterialState.pressed)) { return GoogleFonts.montserrat( textStyle: const TextStyle( - fontSize: 14, color: Colors.white, letterSpacing: 1), + fontSize: 14, + color: Colors.white, + letterSpacing: 1, + ), ); } @@ -113,7 +122,8 @@ class ReadMoreButton extends StatelessWidget { ); }), padding: MaterialStateProperty.all( - const EdgeInsets.symmetric(horizontal: 12, vertical: 16)), + const EdgeInsets.symmetric(horizontal: 12, vertical: 16), + ), ), child: const Text( "READ MORE", @@ -122,7 +132,11 @@ class ReadMoreButton extends StatelessWidget { } } -const Widget divider = Divider(color: Color(0xFFEEEEEE), thickness: 1); +const Widget divider = Divider( + color: Color(0xFFEEEEEE), + thickness: 1, +); + Widget dividerSmall = Container( width: 40, decoration: const BoxDecoration( @@ -135,7 +149,11 @@ Widget dividerSmall = Container( ), ); -List authorSection({String? imageUrl, String? name, String? bio}) { +List authorSection({ + String? imageUrl, + String? name, + String? bio, +}) { return [ divider, Container( @@ -263,21 +281,24 @@ class Footer extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 40), child: const Align( alignment: Alignment.centerRight, - child: TextBody(text: "Copyright © 2020"), + child: TextBody(text: "Copyright © 2022"), ), ); } } class ListItem extends StatelessWidget { + const ListItem({ + Key? key, + required this.title, + this.imageUrl, + this.description, + }) : super(key: key); + + final String? description; + final String? imageUrl; // TODO replace with Post item model. final String title; - final String? imageUrl; - final String? description; - - const ListItem( - {Key? key, required this.title, this.imageUrl, this.description}) - : super(key: key); @override Widget build(BuildContext context) { @@ -330,6 +351,96 @@ class ListItem extends StatelessWidget { * navigation links. Navigation links collapse into * a hamburger menu on screens smaller than 400px. */ + +List headerItems = [ + HeaderItem(title: 'HOME', onTap: () {}), + HeaderItem(title: 'PORTFOLIO', onTap: () {}), + HeaderItem(title: 'STYLE', onTap: () {}), + HeaderItem(title: 'ABOUT', onTap: () {}), + HeaderItem(title: 'CONTACT', onTap: () {}), +]; + +Widget headerLogo(BuildContext context) { + return SizedBox( + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: InkWell( + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + onTap: () => Navigator.popUntil( + context, + ModalRoute.withName(Navigator.defaultRouteName), + ), + child: Text( + "MINIMAL", + style: GoogleFonts.montserrat( + color: textPrimary, + fontSize: ResponsiveValue( + context, + defaultValue: 30, + valueWhen: [ + const Condition.equals(name: TABLET, value: 20.0), + const Condition.largerThan(name: TABLET, value: 35) + ], + ).value!.toDouble(), + letterSpacing: 3, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ); +} + +Widget headerRow(BuildContext context) { + return ResponsiveVisibility( + visible: false, + visibleWhen: const [ + Condition.largerThan(name: MOBILE), + ], + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: headerItems.map( + (headerItem) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + margin: const EdgeInsets.only(right: 30.0), + child: GestureDetector( + onTap: headerItem.title == 'HOME' + ? () => Navigator.popUntil( + context, + ModalRoute.withName(Navigator.defaultRouteName), + ) + : headerItem.title == 'STYLE' + ? () => Navigator.pushNamed(context, Routes.style) + : headerItem.onTap, + child: Text( + headerItem.title, + style: GoogleFonts.montserrat( + color: textPrimary, + fontSize: ResponsiveValue( + context, + defaultValue: 10, + valueWhen: [ + const Condition.largerThan(name: MOBILE, value: 13), + const Condition.largerThan(name: TABLET, value: 17) + ], + ).value!.toDouble(), + letterSpacing: 3, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ); + }, + ).toList(), + ), + ); +} + class MenuBar extends StatelessWidget { const MenuBar({Key? key}) : super(key: key); @@ -342,73 +453,94 @@ class MenuBar extends StatelessWidget { margin: const EdgeInsets.symmetric(vertical: 30), child: Row( children: [ - InkWell( - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, - splashColor: Colors.transparent, - onTap: () => Navigator.popUntil( - context, ModalRoute.withName(Navigator.defaultRouteName)), - child: Text("MINIMAL", - style: GoogleFonts.montserrat( - color: textPrimary, - fontSize: 30, - letterSpacing: 3, - fontWeight: FontWeight.w500)), - ), - Flexible( - child: Container( - alignment: Alignment.centerRight, - child: Wrap( - children: [ - TextButton( - onPressed: () => Navigator.popUntil(context, - ModalRoute.withName(Navigator.defaultRouteName)), - style: menuButtonStyle, - child: const Text( - "HOME", - ), - ), - TextButton( - onPressed: () {}, - style: menuButtonStyle, - child: const Text( - "PORTFOLIO", - ), - ), - TextButton( - onPressed: () => - Navigator.pushNamed(context, Routes.style), - style: menuButtonStyle, - child: const Text( - "STYLE", - ), - ), - TextButton( - onPressed: () {}, - style: menuButtonStyle, - child: const Text( - "ABOUT", - ), - ), - TextButton( - onPressed: () {}, - style: menuButtonStyle, - child: const Text( - "CONTACT", - ), - ), - ], - ), - ), - ), + headerLogo(context), + const Spacer(), + const HeaderMenuTile(), + headerRow(context), + + // visible: false, + // visibleWhen: const [ + // Condition.largerThan(name: MOBILE), + // Condition.largerThan(name: TABLET), + // Condition.largerThan(name: DESKTOP), + // ], + // child: Flexible( + // child: Container( + // alignment: Alignment.centerRight, + // child: Wrap( + // children: [ + // TextButton( + // onPressed: () => Navigator.popUntil( + // + // ), + // style: menuButtonStyle, + // child: const Text( + // "HOME", + // ), + // ), + // TextButton( + // onPressed: () {}, + // style: menuButtonStyle, + // child: const Text( + // "PORTFOLIO", + // ), + // ), + // TextButton( + // onPressed: , + // style: menuButtonStyle, + // child: const Text( + // "STYLE", + // ), + // ), + // TextButton( + // onPressed: () {}, + // style: menuButtonStyle, + // child: const Text( + // "ABOUT", + // ), + // ), + // TextButton( + // onPressed: () {}, + // style: menuButtonStyle, + // child: const Text( + // "CONTACT", + // ), + // ), + // ], + // ), + // ), + // ), + // ), ], ), ), Container( - height: 1, - margin: const EdgeInsets.only(bottom: 30), - color: const Color(0xFFEEEEEE)), + height: 1, + margin: const EdgeInsets.only(bottom: 30), + color: const Color(0xFFEEEEEE), + ), ], ); } } + +class HeaderMenuTile extends StatelessWidget { + const HeaderMenuTile({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ResponsiveVisibility( + hiddenWhen: const [ + Condition.largerThan(name: MOBILE), + ], + child: IconButton( + onPressed: () { + Globals.scaffoldKey.currentState!.openEndDrawer(); + }, + icon: const Icon(Icons.menu), + ), + ); + } +} diff --git a/lib/components/text.dart b/lib/components/text.dart index b875192..2594c36 100644 --- a/lib/components/text.dart +++ b/lib/components/text.dart @@ -24,7 +24,10 @@ class TextBody extends StatelessWidget { class TextBodySecondary extends StatelessWidget { final String text; - const TextBodySecondary({Key? key, required this.text}) : super(key: key); + const TextBodySecondary({ + Key? key, + required this.text, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -41,7 +44,10 @@ class TextBodySecondary extends StatelessWidget { class TextHeadlineSecondary extends StatelessWidget { final String text; - const TextHeadlineSecondary({Key? key, required this.text}) : super(key: key); + const TextHeadlineSecondary({ + Key? key, + required this.text, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -58,14 +64,23 @@ class TextHeadlineSecondary extends StatelessWidget { class TextBlockquote extends StatelessWidget { final String text; - const TextBlockquote({Key? key, required this.text}) : super(key: key); + const TextBlockquote({ + Key? key, + required this.text, + }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: marginBottom24, decoration: const BoxDecoration( - border: Border(left: BorderSide(width: 2, color: Color(0xFF333333)))), + border: Border( + left: BorderSide( + width: 2, + color: Color(0xFF333333), + ), + ), + ), padding: const EdgeInsets.only(left: 20), child: Align( alignment: Alignment.centerLeft, @@ -79,8 +94,12 @@ class TextBlockquote extends StatelessWidget { } ButtonStyle? menuButtonStyle = TextButton.styleFrom( - backgroundColor: Colors.transparent, - onSurface: null, - primary: textSecondary, - textStyle: buttonTextStyle, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16)); + backgroundColor: Colors.transparent, + //onSurface: null, + //primary: textSecondary, + textStyle: buttonTextStyle, + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), +); diff --git a/lib/components/typography.dart b/lib/components/typography.dart index 92ad76f..8ff35b4 100644 --- a/lib/components/typography.dart +++ b/lib/components/typography.dart @@ -4,24 +4,44 @@ import 'package:minimal/components/color.dart'; // Simple TextStyle headlineTextStyle = GoogleFonts.montserrat( - textStyle: const TextStyle( - fontSize: 26, - color: textPrimary, - letterSpacing: 1.5, - fontWeight: FontWeight.w300)); + textStyle: const TextStyle( + fontSize: 26, + color: textPrimary, + letterSpacing: 1.5, + fontWeight: FontWeight.w300, + ), +); TextStyle headlineSecondaryTextStyle = GoogleFonts.montserrat( - textStyle: const TextStyle( - fontSize: 20, color: textPrimary, fontWeight: FontWeight.w300)); + textStyle: const TextStyle( + fontSize: 20, + color: textPrimary, + fontWeight: FontWeight.w300, + ), +); TextStyle subtitleTextStyle = GoogleFonts.openSans( - textStyle: const TextStyle(fontSize: 14, color: textSecondary, letterSpacing: 1)); + textStyle: const TextStyle( + fontSize: 14, + color: textSecondary, + letterSpacing: 1, + ), +); TextStyle bodyTextStyle = GoogleFonts.openSans( - textStyle: const TextStyle(fontSize: 14, color: textPrimary)); + textStyle: const TextStyle( + fontSize: 14, + color: textPrimary, + ), +); TextStyle buttonTextStyle = GoogleFonts.montserrat( - textStyle: const TextStyle(fontSize: 14, color: textPrimary, letterSpacing: 1)); + textStyle: const TextStyle( + fontSize: 14, + color: textPrimary, + letterSpacing: 1, + ), +); // Advanced // TODO: Add additional text styles. diff --git a/lib/main.dart b/lib/main.dart index 4c19cbe..493c7e5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,32 +14,38 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( builder: (context, child) => ResponsiveWrapper.builder( - BouncingScrollWrapper.builder(context, child!), - maxWidth: 1200, - minWidth: 450, - defaultScale: true, - breakpoints: [ - const ResponsiveBreakpoint.resize(450, name: MOBILE), - const ResponsiveBreakpoint.autoScale(800, name: TABLET), - const ResponsiveBreakpoint.autoScale(1000, name: TABLET), - const ResponsiveBreakpoint.resize(1200, name: DESKTOP), - const ResponsiveBreakpoint.autoScale(2460, name: "4K"), - ], - background: Container(color: const Color(0xFFF5F5F5))), + BouncingScrollWrapper.builder(context, child!), + maxWidth: 1200, + minWidth: 450, + defaultScale: true, + breakpoints: [ + const ResponsiveBreakpoint.resize(450, name: MOBILE), + const ResponsiveBreakpoint.autoScale(800, name: TABLET), + const ResponsiveBreakpoint.autoScale(1000, name: TABLET), + const ResponsiveBreakpoint.resize(1200, name: DESKTOP), + const ResponsiveBreakpoint.autoScale(2460, name: "4K"), + ], + background: Container( + color: const Color(0xFFF5F5F5), + ), + ), initialRoute: Routes.home, onGenerateRoute: (RouteSettings settings) { - return Routes.fadeThrough(settings, (context) { - switch (settings.name) { - case Routes.home: - return const ListPage(); - case Routes.post: - return const PostPage(); - case Routes.style: - return const TypographyPage(); - default: - return const SizedBox.shrink(); - } - }); + return Routes.fadeThrough( + settings, + (context) { + switch (settings.name) { + case Routes.home: + return const ListPage(); + case Routes.post: + return const PostPage(); + case Routes.style: + return const TypographyPage(); + default: + return const SizedBox.shrink(); + } + }, + ); }, debugShowCheckedModeBanner: false, ); diff --git a/lib/models/header_model.dart b/lib/models/header_model.dart new file mode 100644 index 0000000..1b280d8 --- /dev/null +++ b/lib/models/header_model.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class HeaderItem { + final String title; + final VoidCallback onTap; + final BuildContext? context; + + HeaderItem({ + required this.title, + required this.onTap, + this.context, + }); +} diff --git a/lib/pages/page_list.dart b/lib/pages/page_list.dart index 20ff71f..bc2dc03 100644 --- a/lib/pages/page_list.dart +++ b/lib/pages/page_list.dart @@ -12,6 +12,36 @@ class ListPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( + endDrawer: Drawer( + width: 170, + backgroundColor: Colors.white, + child: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 24.0, + ), + child: ListView.separated( + itemBuilder: (BuildContext context, int index) { + return ListTile( + title: Text( + headerItems[index].title, + style: const TextStyle( + color: textPrimary, + ), + ), + ); + }, + separatorBuilder: (BuildContext context, int index) { + return const SizedBox( + height: 10.0, + ); + }, + itemCount: headerItems.length, + ), + ), + ), + ), body: Stack( children: [ SingleChildScrollView( @@ -21,34 +51,38 @@ class ListPage extends StatelessWidget { children: [ const MenuBar(), const ListItem( - imageUrl: - "assets/images/paper_flower_overhead_bw_w1080.jpg", - title: listItemTitleText, - description: listItemPreviewText), + imageUrl: + "assets/images/paper_flower_overhead_bw_w1080.jpg", + title: listItemTitleText, + description: listItemPreviewText, + ), divider, const ListItem( - imageUrl: - "assets/images/iphone_cactus_tea_overhead_bw_w1080.jpg", - title: listItemTitleText, - description: listItemPreviewText), + imageUrl: + "assets/images/iphone_cactus_tea_overhead_bw_w1080.jpg", + title: listItemTitleText, + description: listItemPreviewText, + ), divider, const ListItem( - imageUrl: - "assets/images/typewriter_overhead_bw_w1080.jpg", - title: listItemTitleText, - description: listItemPreviewText), + imageUrl: "assets/images/typewriter_overhead_bw_w1080.jpg", + title: listItemTitleText, + description: listItemPreviewText, + ), divider, const ListItem( - imageUrl: - "assets/images/coffee_paperclips_pencil_angled_bw_w1080.jpg", - title: listItemTitleText, - description: listItemPreviewText), + imageUrl: + "assets/images/coffee_paperclips_pencil_angled_bw_w1080.jpg", + title: listItemTitleText, + description: listItemPreviewText, + ), divider, const ListItem( - imageUrl: - "assets/images/joy_note_coffee_eyeglasses_overhead_bw_w1080.jpg", - title: listItemTitleText, - description: listItemPreviewText), + imageUrl: + "assets/images/joy_note_coffee_eyeglasses_overhead_bw_w1080.jpg", + title: listItemTitleText, + description: listItemPreviewText, + ), divider, Container( padding: const EdgeInsets.symmetric(vertical: 80), diff --git a/lib/pages/page_post.dart b/lib/pages/page_post.dart index 5cd0f6d..d89d048 100644 --- a/lib/pages/page_post.dart +++ b/lib/pages/page_post.dart @@ -64,11 +64,13 @@ class PostPage extends StatelessWidget { ), const Align( alignment: Alignment.centerLeft, - child: TagWrapper(tags: [ - Tag(tag: "Writing"), - Tag(tag: "Photography"), - Tag(tag: "Development") - ]), + child: TagWrapper( + tags: [ + Tag(tag: "Writing"), + Tag(tag: "Photography"), + Tag(tag: "Development") + ], + ), ), ...authorSection( imageUrl: "assets/images/avatar_default.png", diff --git a/lib/routes.dart b/lib/routes.dart index 62b6aa6..b1bad94 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -3,8 +3,8 @@ import 'package:flutter/widgets.dart'; class Routes { static const String home = "/"; - static const String post = "post"; - static const String style = "style"; + static const String post = "/post"; + static const String style = "/style"; static Route fadeThrough(RouteSettings settings, WidgetBuilder page, {int duration = 300}) { diff --git a/lib/utils/globals.dart b/lib/utils/globals.dart new file mode 100644 index 0000000..1cc2844 --- /dev/null +++ b/lib/utils/globals.dart @@ -0,0 +1,5 @@ +import 'package:flutter/material.dart'; + +class Globals { + static GlobalKey scaffoldKey = GlobalKey(); +}