|
1 |
| -# responsive_layout |
2 |
| - |
3 |
| -A new Flutter package project. |
4 |
| - |
5 |
| -## Getting Started |
6 |
| - |
7 |
| -TODO: |
8 |
| -- [] rename examples; |
9 |
| -- [] `granular_layout` sub-package; |
10 |
| - - Add granular examples to pre-existing ones. |
11 |
| -- [] In-depth usage example; |
12 |
| -- [] Multiple resolvers example; |
13 |
| -- [] lint + vscode + strict; |
14 |
| -- [] README/Contributing/Changelog/Architecture; |
15 |
| -- [] Unit/Integration tests. |
16 |
| - |
17 |
| -This project is a starting point for a Dart |
18 |
| -[package](https://flutter.dev/developing-packages/), |
19 |
| -a library module containing code that can be shared easily across |
20 |
| -multiple Flutter or Dart projects. |
21 |
| - |
22 |
| -For help getting started with Flutter, view our |
23 |
| -[online documentation](https://flutter.dev/docs), which offers tutorials, |
24 |
| -samples, guidance on mobile development, and a full API reference. |
| 1 | +# layoutr |
| 2 | + |
| 3 | +[](https://pub.dev/packages/layoutr) |
| 4 | + |
| 5 | +A set of succinct Dart/Flutter utilities to help doing responsive layouts with less verbosity. |
| 6 | + |
| 7 | +## Why |
| 8 | + |
| 9 | +There are already a ton of layout/UI libraries out there and this is just another one to solve the same problem: responsive layouts. |
| 10 | + |
| 11 | +This package aims to be a single dependecy to handle responsive use-cases in Flutter applications while keeping the verbosity to a minimum, because we know how quickly verbose (reads harder-to-read) we can get with those Widgets. |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +Before finding out the package's API, make sure to [install this package](https://pub.dev/packages/layoutr/install). With this out of the way, let's get an overview what this package does: |
| 16 | + |
| 17 | +### Layout Resolvers |
| 18 | + |
| 19 | +These are the core layout classes that should make the process of handling multiple-layout widgets much less verbose. We can use these resolvers whenever you have a `BuildContext` (usually in the Widgets `build` function) available. |
| 20 | + |
| 21 | +Even though the magic should happen inside the abstract `LayoutResolver`, we need to extend this class to provide our desired `Breakpoint`s. This is intended due to the fact that there is an incredible amount of use-cases available when building any kind of UI - meaning that these breakpoints are completely subjective, based on each project "constraints". |
| 22 | + |
| 23 | +But that's not any reason to not have built-in Layout Resolvers, and these will probably fit the most generic use-cases. To exemplify the resolvers, we can see how the `CommonLayout` works. |
| 24 | + |
| 25 | +#### Exemplifying with CommonLayout |
| 26 | + |
| 27 | +The `CommonLayout` is split in 4 breakpoints: `desktop`, `tablet`, `phone` and `tinyHardware`. A simple usage of returning a responsive `Text` widget that has both its value and style customized based on the current device's constraints may be done like the below: |
| 28 | +```dart |
| 29 | +import 'package:layoutr/common_layout.dart'; |
| 30 | +
|
| 31 | +// ... |
| 32 | +
|
| 33 | +Widget build(BuildContext context) { |
| 34 | + final layout = context.commonLayout; |
| 35 | + final textTheme = Theme.of(context).textTheme; |
| 36 | +
|
| 37 | + return layout.value( |
| 38 | + desktop: () => Text('Desktop layout', style: textTheme.headline1), |
| 39 | + phone: () => Text('Phone layout', style: textTheme.headline4), |
| 40 | + tinyHardware: () => Text('Tiny Hardware layout', style: textTheme.headline6), |
| 41 | + ); |
| 42 | +} |
| 43 | +// ... |
| 44 | +``` |
| 45 | + |
| 46 | +> You can see that there is no `tablet` supplied to the `layout.value`, and that is intended to exemplify a common scenario, where we may want to just provide two or three arguments - and that means not all possible scenarios are "covered" - and that's where the resolver comes in handy: if the **current breakpoint** value is not passed to `layout.value`, it will fallback to the "nearest" available one, fitting the most suitable layout for your particular value. This "nearest logic" can be confusing, but you can find out more how it works in `LayoutResolver.closestValue` |
| 47 | +
|
| 48 | +Also, if you want to keep the structure but want to override the sizes of each breakpoint, you can provide your `CommonLayout` instance through the InheritedWidget called `CommonLayoutWidget`: |
| 49 | + |
| 50 | +```dart |
| 51 | +import 'package:layoutr/common_layout.dart'; |
| 52 | +
|
| 53 | +// ... |
| 54 | +
|
| 55 | +CommonLayoutWidget( |
| 56 | + resolver: CommonLayout(context.deviceWidth, desktop: 800), |
| 57 | + child: /* My child */, |
| 58 | +); |
| 59 | +// ... |
| 60 | +``` |
| 61 | + |
| 62 | +> You will probably want to add these above the top-most widget of your tree, usually the `MaterialApp`, but be careful that `MediaQuery` may not be available if you don't have any other widget above `CommonLayoutWidget`, which is required when we use the `context.deviceWidth`. You can check out an [`example/`](https://pub.dev/packages/layoutr/example#Common-Layout-Inherited-Widget) that overrides the custom values. |
| 63 | +
|
| 64 | +Other than `layout.value`, the `CommonLayout` provide utilities for simple boolean comparisons: |
| 65 | + |
| 66 | +```dart |
| 67 | +import 'package:layoutr/common_layout.dart'; |
| 68 | +
|
| 69 | +// ... |
| 70 | +
|
| 71 | +Widget build(BuildContext context) { |
| 72 | + final layout = context.commonLayout; |
| 73 | + const pageTitle = 'Title'; |
| 74 | +
|
| 75 | + final myAppBar = layout.isTabletOrSmaller ? AppBar(title: const Text(pageTitle)) : null; |
| 76 | +
|
| 77 | + return Scaffold( |
| 78 | + // We wan't to have an `AppBar` if the current layout is a tablet or smaller |
| 79 | + appBar: myAppBar, |
| 80 | + body: Center( |
| 81 | + child: Column( |
| 82 | + children: [ |
| 83 | + // And we wan't to have a custom title `AppBar` if the current layout is a tablet or smaller |
| 84 | + if (layout.isDesktop) |
| 85 | + Padding( |
| 86 | + padding: const EdgeInsets.only(bottom: 40), |
| 87 | + child: Text(pageTitle, style: Theme.of(context).textTheme.headline3), |
| 88 | + ), |
| 89 | + // ... the rest of the widget |
| 90 | + ], |
| 91 | + ), |
| 92 | + ), |
| 93 | + ); |
| 94 | +} |
| 95 | +// ... |
| 96 | +``` |
| 97 | + |
| 98 | +Tip: we can easily use this package with common libraries like `provider` (see in [`example/`](https://pub.dev/packages/layoutr/example#`provider`)) and `river_pod` (see in [`example/`](https://pub.dev/packages/layoutr/example#`river_pod`)). |
| 99 | + |
| 100 | +#### Available Built-in `LayoutResolver` |
| 101 | +- `CommonLayout`: a resolver split in 4 breakpoints: `desktop`, `tablet`, `phone` and `tinyHardware`. Import this resolver through `package:layoutr/common_layout.dart` - (see in [`example/`](https://pub.dev/packages/layoutr/example#Common-Layout)); |
| 102 | +- `GranularLayout`: a resolver split in 6 breakpoints: `xxLarge`, `xLarge`, `large`, `medium`, `small` and `xSmall`. Import this resolver through `package:layoutr/granular_layout.dart` - (see in [`example/`](https://pub.dev/packages/layoutr/example#Granular-Layout)). |
| 103 | + |
| 104 | +### Custom `LayoutResolver` |
| 105 | + |
| 106 | +If the above built-in resolvers don't match the requirements, `LayoutResolver` can be customized by extending it, taking advantage of the utilities built-in in the abstract class itself. To extend the and implement your custom `LayoutResolver`, import `package:layoutr/layoutr.dart`. Check out a custom implementation example in the [`example/`](https://pub.dev/packages/layoutr/example#Custom-Layout). |
| 107 | + |
| 108 | +### Utilities |
| 109 | + |
| 110 | +- Syntax-sugar for common use-cases, like: `deviceWidth` and `deviceHeight` (plus a couple more that are WIP); |
| 111 | +- Other helper Widgets are WIP. |
| 112 | + |
| 113 | +## Reference OS Projects |
| 114 | + |
| 115 | +List of open source projects that use this package and can be used as a reference to implement your own use-cases: |
| 116 | + |
| 117 | +WIP |
| 118 | + |
| 119 | +## Contributing |
| 120 | + |
| 121 | +There is no rocket science to contributing to this project. Just open your issue or pull request if needed - but please be descriptive! |
| 122 | + |
| 123 | +To submit a PR, follow [these useful guidelines](https://gist.github.com/MarcDiethelm/7303312), respect the dartfmt lint (modifications/exceptions may be discussed), and create an awesome description so we can understand it. |
| 124 | + |
| 125 | +Even though there is no "rules" to how you should contribute, there goes my quick tips on it: |
| 126 | + |
| 127 | +- If you're experiencing a bug that can be demonstrated visually, please take screenshots and post them alongside the issue; |
| 128 | +- For a quicker/seamless understanding of the issue, please post a sample project, so the evaluation will be bulletproof; |
| 129 | +- This is a git overall tip - do atomic commits with a descriptive message (no more than 50 characters is ideal). |
0 commit comments