{"id":18545905,"url":"https://github.com/olmps/layoutr","last_synced_at":"2025-04-09T19:32:19.856Z","repository":{"id":61973918,"uuid":"299996598","full_name":"olmps/layoutr","owner":"olmps","description":"Layout Resolver (layoutr)- A set of succinct Dart/Flutter utilities to help doing responsive layouts with less verbosity. https://pub.dev/packages/layoutr","archived":false,"fork":false,"pushed_at":"2021-03-12T15:28:43.000Z","size":148,"stargazers_count":10,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T11:11:59.010Z","etag":null,"topics":["dart","flutter","package","responsive-layout"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olmps.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-30T17:19:54.000Z","updated_at":"2023-10-07T02:53:36.000Z","dependencies_parsed_at":"2022-10-24T13:31:02.299Z","dependency_job_id":null,"html_url":"https://github.com/olmps/layoutr","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olmps%2Flayoutr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olmps%2Flayoutr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olmps%2Flayoutr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olmps%2Flayoutr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olmps","download_url":"https://codeload.github.com/olmps/layoutr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248097945,"owners_count":21047341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","package","responsive-layout"],"created_at":"2024-11-06T20:22:48.869Z","updated_at":"2025-04-09T19:32:18.373Z","avatar_url":"https://github.com/olmps.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# layoutr\n\n[![pub package](https://img.shields.io/pub/v/layoutr?style=flat-square)](https://pub.dev/packages/layoutr)\n\nA set of succinct Dart/Flutter utilities to help doing responsive layouts with less verbosity.\n\n## Why\n\nThere are already a ton of layout/UI libraries out there and this is just another one to solve the same problem: responsive layouts.\n\nThis 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.\n\n## Usage\n\nBefore 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:\n\n### Layout Resolvers\n\nThese 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.\n\nEven 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\".\n\nBut 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.\n\n#### Exemplifying with CommonLayout\n---\n\nBefore diving into the concepts, you have to make sure to provide a `CommonLayout` for your widgets. This can be done\nin multiple different ways, although the easiest is to just wrap your top-most widget in the `CommonLayoutWidget`:\n\n```dart\nimport 'package:layoutr/common_layout.dart';\n\n// ...\nCommonLayoutWidget(\n  child: /* My child, possibly a MaterialApp/WidgetsApp */,\n);\n// ...\n```\n\nNow, `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:\n\n```dart\nimport 'package:layoutr/common_layout.dart';\n\n// ...\n\nWidget build(BuildContext context) {\n  final layout = context.commonLayout;\n  final textTheme = Theme.of(context).textTheme;\n\n  return layout.value(\n    desktop: () =\u003e Text('Desktop layout', style: textTheme.headline1),\n    phone: () =\u003e Text('Phone layout', style: textTheme.headline4),\n    tinyHardware: () =\u003e Text('Tiny Hardware layout', style: textTheme.headline6),\n  );\n}\n// ...\n```\n\n\u003e **TLDR: all breakpoints don't need to be provided, the layout will automatically find the nearest suitable value for your current breakpoint.**\n\u003e \n\u003e Long version: 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`\n\nOther than `layout.value`, the `CommonLayout` provide utilities for simple boolean comparisons:\n\n```dart\nimport 'package:layoutr/common_layout.dart';\n\n// ...\n\nWidget build(BuildContext context) {\n  final layout = context.commonLayout;\n  const pageTitle = 'Title';\n\n  final myAppBar = layout.isTabletOrSmaller ? AppBar(title: const Text(pageTitle)) : null;\n\n  return Scaffold(\n    // We wan't to have an `AppBar` if the current layout is a tablet or smaller\n    appBar: myAppBar,\n    body: Center(\n      child: Column(\n        children: [\n          // And we wan't to have a custom title `AppBar` if the current layout is a tablet or smaller\n          if (layout.isDesktop)\n            Text(pageTitle, style: Theme.of(context).textTheme.headline3),\n          // ... the rest of the widget\n        ],\n      ),\n    ),\n  );\n}\n// ...\n```\n\n##### Overriding `CommonLayout` resolver\n---\n\nTo override the sizes for each breakpoint, you can provide your own `CommonLayout` instance:\n\n```dart\nimport 'package:layoutr/common_layout.dart';\n// ...\nCommonLayoutWidget(\n  resolverBuilder: (constraints) =\u003e CommonLayout(constraints.maxWidth, desktop: 800, tablet: 520),\n  child: /* My child */,\n);\n// ...\n```\n\n\u003e **TLDR: your app don't need to necessarily use `MaterialApp` (or `WidgetsApp`), just add it above your top-most widget.**\n\u003e \n\u003e Long version: it's probably the best to add the resolver widget above the top-most widget of your tree, because the all built-in widgets use a `LayoutBuilder` to provide such responsive features, and this may create inconsistencies if they are created in nested widgets, which will only use the parent's `BoxConstraints`. This could also be useful if you wanted to created a resolver that is not necessarily related to the device's sizes, but I fail to see a useful scenario like this at the moment.\n\n##### Overriding `CommonLayout` spacings\n---\nTo override the spacings for each breakpoint, you can provide your own `RawSpacings` instance:\n\n```dart\nimport 'package:layoutr/common_layout.dart';\n// ...\nCommonLayoutWidget(\n  spacings: const RawSpacings(4, 12, 20, 24, 32, 40, 48, 56, 60),\n  child: /* My child */,\n);\n// ...\n```\n\nWhile the above will customize all spacings for all breakpoints, it's still not responsive, and that's fine. You may\nwant to use the spacings for the sole benefit of type-safety utilities. Now if you also want them to be responsive,\nthere is a constructor for that:\n\n```dart\nimport 'package:layoutr/common_layout.dart';\n\n// ...\nCommonLayoutWidget.withResponsiveSpacings(\n  desktopSpacings: RawSpacings(8, 16, 24, 32, 40, 52, 60, 68, 80),\n  phoneSpacings: RawSpacings(4, 8, 12, 20, 28, 36, 40, 48, 60),\n  child: /* My child */,\n);\n// ...\n```\n\nNot sure what spacings mean? Check out [spacings](#spacings).\n\n----\nTips:\n- we can easily use this package with common libraries like `provider` (see in [`example/`](example/provider_usage/)) and `river_pod` (see in [`example/`](example/river_pod_usage/`));\n- everything explained here is same for all built-in resolvers (like `GranularLayout`), they just differ in the\nbreakpoints amount/types.\n\n#### Available Built-in `LayoutResolver`\n---\n\n- `CommonLayout`: a resolver split in 4 breakpoints: `desktop`, `tablet`, `phone` and `tinyHardware`. Import this resolver through `package:layoutr/common_layout.dart` - (see in [`example/`](example/common_layout/));\n- `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/`](example/granular_layout/)).\n\n#### Custom `LayoutResolver`\n---\n\nIf 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/`](example/custom_layout/).\n\n### Spacings\n\nAll spacing features revolve around `Spacing` enumerator. These are named ranges that should usually fit most use-cases\nout there in the wild. They are pretty intuitive: `xxxSmall`, `xxSmall`, `xSmall`, `small`, `medium`, ... and so on.\n\nSpacings are an additional help for situations where we need type-safety, responsivity, or both. Being more specific:\n- type-safety: even if you don't want to use it as a responsive value, you will still benefit from having a type-safe\nsystem that will prevent inconsistent spacings, margins and paddings in your application;\n- responsivity: the spacings can be customized to one, some or all breakpoints, meaning that you won't have to change\nanything if you use the `Spacing` type system.\n\nAll built-in `LayoutResolver` will provide the spacings out-of-the-box, so no need to add anything extra other than your root widget. But this might not be the best for most. If wanted, one could use only this package's feature alone (with `SpacingsInheritedWidget`), like so:\n\n```dart\nimport 'package:layoutr/layoutr.dart';\n\n// ...\nLayoutBuilder(\n  builder: (context, constraints) {\n    // If we wanted, we could use this spacings type-safety/responsivity without the resolver themselves\n    final myCustomSpacings = constraints.maxWidth \u003e 800\n        ? RawSpacings(16, 24, 32, 40, 52, 60, 68, 76, 80)\n        : RawSpacings(8, 12, 24, 32, 40, 48, 56, 64, 72);\n\n    return SpacingsInheritedWidget(\n      spacings: myCustomSpacings,\n      child: // ... ,\n    );\n  },\n);\n// ...\n```\n\nAlone this can be somewhat useful, but its potential is enhanced with the built-in utilities and resolvers\nwidgets - you can even build your own by extending any `BuildContext`, `Widget` or any layout-related element.\n\nAn example usage with some of the utilities:\n\n```dart\nimport 'package:layoutr/layoutr.dart';\n\n// SpacingMixin just helps you to call spacings like `smallSpacing`, instead of `Spacing.smallSpacing`, making it\n// overall less verbose.\nclass MyPage extends StatelessWidget with SpacingMixin {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: Column(\n        children: [\n          Text('My page'),\n          // Creates a vertical responsive/type-safe spacing\n          context.verticalBox(xxxLargeSpacing), \n          Container(\n            // Creates a vertical symmetrical insets responsive/type-safe spacing\n            margin: context.symmetricInsets(vertical: smallSpacing),\n            color: Colors.amber,\n            // Wraps this widget in a Padding with all insets to this responsive/type-safe spacing\n            child: Placeholder().withAllPadding(context, mediumSpacing),\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\n### Utilities\n\n- Syntax-sugar for common use-cases, like: `deviceWidth` and `deviceHeight`;\n- [Helpers] for commonly used elements like `EdgeInsets` and `Padding`, using `Spacing`.\n\nCheck out all [spacing utilities](lib/src/utilities.dart).\n\n## Reference OS Projects\n\nList of open source projects that use this package and can be used as a reference to implement your own use-cases:\n\nWIP\n\n## Contributing\n\nWant to contribute? Check it out [here](CONTRIBUTING.md).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folmps%2Flayoutr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folmps%2Flayoutr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folmps%2Flayoutr/lists"}