flexify 3.0.0 copy "flexify: ^3.0.0" to clipboard
flexify: ^3.0.0 copied to clipboard

Responsive Flutter UI scaled from your design size with .rw, .rh, .rt and .rs, plus navigation without BuildContext and 14 page transitions.

example/lib/main.dart

// flexify by Azerosoft (https://azerosoft.com)
// Licensed under the MIT License. See the LICENSE file for details.

import 'package:flexify/flexify.dart';
import 'package:flutter/material.dart';

/// Azerosoft brand colours.
const Color azerosoftBlue = Color(0xFF2C55F0);
const Color azerosoftBlueDark = Color(0xFF7390FF);

/// The screen size the demo was designed for.
const double designWidth = 375;
const double designHeight = 812;

const Duration transitionDuration = Duration(milliseconds: 600);

void main() {
  runApp(const FlexifyDemoApp());
}

class FlexifyDemoApp extends StatelessWidget {
  const FlexifyDemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Flexify(
      designWidth: designWidth,
      designHeight: designHeight,
      // Keeps text and heights at their portrait size in landscape.
      matchOrientation: true,
      app: MaterialApp(
        title: 'Flexify',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: azerosoftBlue).copyWith(
            primary: azerosoftBlue,
            onPrimary: Colors.white,
          ),
        ),
        darkTheme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(
            seedColor: azerosoftBlue,
            brightness: Brightness.dark,
          ).copyWith(
            primary: azerosoftBlueDark,
            onPrimary: const Color(0xFF0D1016),
          ),
        ),
        home: const HomeScreen(),
      ),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String? _city;

  Future<void> _pickCity() async {
    // The picker returns the city it was closed with, like Navigator.push.
    final String? city = await Flexify.go<String>(
      const CityPickerScreen(),
      animation: FlexifyRouteAnimations.slideFromBottom,
      settings: const RouteSettings(name: '/city'),
    );
    if (city != null) {
      setState(() => _city = city);
    }
  }

  @override
  Widget build(BuildContext context) {
    final Size screen = Flexify.screenSize;

    return Scaffold(
      appBar:
          AppBar(title: Text('Flexify', style: _style(20, FontWeight.w600))),
      body: ListView(
        padding: EdgeInsets.all(16.rw),
        children: <Widget>[
          _Section(
            title: 'Responsive sizes',
            children: <Widget>[
              // In landscape, matchOrientation compares the screen with the
              // design turned on its side.
              _InfoRow(
                'Design size',
                '${(screen.width / Flexify.scaleWidth).round()} × '
                    '${(screen.height / Flexify.scaleHeight).round()}',
              ),
              _InfoRow(
                'This screen',
                '${screen.width.round()} × ${screen.height.round()}',
              ),
              _InfoRow('Screen type', Flexify.screenType.name),
              _InfoRow('Width scale', Flexify.scaleWidth.toStringAsFixed(2)),
              _InfoRow('Height scale', Flexify.scaleHeight.toStringAsFixed(2)),
              12.verticalSpace,
              Row(
                children: <Widget>[
                  _Box(width: 200.rw, height: 72.rh, label: '200.rw × 72.rh'),
                  12.horizontalSpace,
                  _Box(width: 72.rs, height: 72.rs, label: '72.rs'),
                ],
              ),
              12.verticalSpace,
              // Percentages of the whole screen, not of the design.
              _Box(width: 50.sw, height: 6.sh, label: '50.sw × 6.sh'),
            ],
          ),
          16.verticalSpace,
          _Section(
            title: 'Page transitions',
            children: <Widget>[
              Text('Tap one to open a page with it.', style: _style(14)),
              12.verticalSpace,
              Wrap(
                spacing: 8.rw,
                runSpacing: 8.rh,
                children: <Widget>[
                  for (final FlexifyRouteAnimations animation
                      in FlexifyRouteAnimations.values)
                    OutlinedButton(
                      onPressed: () => Flexify.go(
                        TransitionScreen(animation: animation),
                        animation: animation,
                        duration: transitionDuration,
                      ),
                      child: Text(animation.name, style: _style(13)),
                    ),
                ],
              ),
            ],
          ),
          16.verticalSpace,
          _Section(
            title: 'Results',
            children: <Widget>[
              Text(
                'A page can return a value to the page that opened it.',
                style: _style(14),
              ),
              12.verticalSpace,
              _InfoRow('Picked city', _city ?? 'none yet'),
              12.verticalSpace,
              ElevatedButton(
                onPressed: _pickCity,
                child: Text('Pick a city', style: _style(15)),
              ),
            ],
          ),
          24.verticalSpace,
          const _MadeByAzerosoft(),
        ],
      ),
    );
  }
}

class TransitionScreen extends StatelessWidget {
  const TransitionScreen({super.key, required this.animation});

  final FlexifyRouteAnimations animation;

  @override
  Widget build(BuildContext context) {
    const List<FlexifyRouteAnimations> all = FlexifyRouteAnimations.values;
    final FlexifyRouteAnimations next = all[(animation.index + 1) % all.length];
    final ColorScheme colors = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: Text(animation.name, style: _style(20, FontWeight.w600)),
      ),
      body: Padding(
        padding: EdgeInsets.all(24.rw),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            const Spacer(),
            Icon(Icons.auto_awesome, size: 72.rs, color: colors.primary),
            16.verticalSpace,
            Text(
              'Opened with FlexifyRouteAnimations.${animation.name}',
              textAlign: TextAlign.center,
              style: _style(18, FontWeight.w600),
            ),
            8.verticalSpace,
            Text(
              _descriptions[animation]!,
              textAlign: TextAlign.center,
              style: _style(14).copyWith(color: colors.onSurfaceVariant),
            ),
            const Spacer(),
            ElevatedButton(
              onPressed: Flexify.back,
              child: Text('Back', style: _style(15)),
            ),
            8.verticalSpace,
            OutlinedButton(
              onPressed: () => Flexify.goRemove(
                TransitionScreen(animation: next),
                animation: next,
                duration: transitionDuration,
              ),
              child: Text('Replace with ${next.name}', style: _style(15)),
            ),
            8.verticalSpace,
            TextButton(
              onPressed: Flexify.backToFirst,
              child: Text('Back to the first page', style: _style(15)),
            ),
          ],
        ),
      ),
    );
  }
}

class CityPickerScreen extends StatelessWidget {
  const CityPickerScreen({super.key});

  static const List<String> cities = <String>[
    'Baku',
    'Ganja',
    'Shaki',
    'Shusha',
    'Lankaran',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Pick a city', style: _style(20, FontWeight.w600)),
      ),
      body: ListView(
        padding: EdgeInsets.symmetric(vertical: 8.rh),
        children: <Widget>[
          for (final String city in cities)
            ListTile(
              title: Text(city, style: _style(16)),
              trailing: Icon(Icons.chevron_right, size: 24.rs),
              // Closes the picker and gives the city to the page below.
              onTap: () => Flexify.back(city),
            ),
        ],
      ),
    );
  }
}

const Map<FlexifyRouteAnimations, String> _descriptions =
    <FlexifyRouteAnimations, String>{
  FlexifyRouteAnimations.fade: 'Fades the page in.',
  FlexifyRouteAnimations.slide: 'Slides the page in from the right.',
  FlexifyRouteAnimations.scale: 'Grows the page from the center.',
  FlexifyRouteAnimations.rotate: 'Spins the page in by one full turn.',
  FlexifyRouteAnimations.zoom: 'Grows the page at a constant speed.',
  FlexifyRouteAnimations.size: 'Reveals the page from the middle outwards.',
  FlexifyRouteAnimations.elastic: 'Grows the page with an elastic bounce.',
  FlexifyRouteAnimations.flip: 'Flips the page around its vertical axis.',
  FlexifyRouteAnimations.slideFromBottom: 'Slides the page up from the bottom.',
  FlexifyRouteAnimations.fadeAndScale:
      'Fades the page in while it grows from 80 % of its size.',
  FlexifyRouteAnimations.blur: 'Fades the page in over a blurred background.',
  FlexifyRouteAnimations.slideAndFade:
      'Slides the page in from the right while fading it in.',
  FlexifyRouteAnimations.rotateAndScale:
      'Spins and grows the page at the same time.',
  FlexifyRouteAnimations.flipAndFade: 'Flips the page while fading it in.',
};

TextStyle _style(double fontSize, [FontWeight? weight]) {
  return TextStyle(fontSize: fontSize.rt, fontWeight: weight);
}

class _Section extends StatelessWidget {
  const _Section({required this.title, required this.children});

  final String title;
  final List<Widget> children;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16.rw),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(title, style: _style(16, FontWeight.w600)),
            12.verticalSpace,
            ...children,
          ],
        ),
      ),
    );
  }
}

class _InfoRow extends StatelessWidget {
  const _InfoRow(this.label, this.value);

  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 4.rh),
      child: Row(
        children: <Widget>[
          Expanded(child: Text(label, style: _style(14))),
          Text(value, style: _style(14, FontWeight.w600)),
        ],
      ),
    );
  }
}

class _Box extends StatelessWidget {
  const _Box({required this.width, required this.height, required this.label});

  final double width;
  final double height;
  final String label;

  @override
  Widget build(BuildContext context) {
    final ColorScheme colors = Theme.of(context).colorScheme;
    return Container(
      width: width,
      height: height,
      alignment: Alignment.center,
      decoration: BoxDecoration(
        color: colors.primary,
        borderRadius: BorderRadius.circular(12.rs),
      ),
      child: Text(
        label,
        textAlign: TextAlign.center,
        style: _style(12, FontWeight.w600).copyWith(color: colors.onPrimary),
      ),
    );
  }
}

/// "by Azerosoft" signature with the Azerosoft mark.
class _MadeByAzerosoft extends StatelessWidget {
  const _MadeByAzerosoft();

  @override
  Widget build(BuildContext context) {
    final Color color = Theme.of(context).colorScheme.onSurfaceVariant;
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Container(
          width: 20.rs,
          height: 20.rs,
          alignment: Alignment.center,
          decoration: BoxDecoration(
            color: azerosoftBlue,
            borderRadius: BorderRadius.circular(5.rs),
          ),
          child: Text(
            'a.',
            style: TextStyle(
              color: Colors.white,
              fontSize: 12.rt,
              fontWeight: FontWeight.w700,
              height: 1,
            ),
          ),
        ),
        8.horizontalSpace,
        Text(
          'flexify by Azerosoft · azerosoft.com',
          style: _style(12).copyWith(color: color),
        ),
      ],
    );
  }
}
48
likes
160
points
143
downloads
screenshot

Documentation

API reference

Publisher

verified publisherazerosoft.com

Weekly Downloads

Responsive Flutter UI scaled from your design size with .rw, .rh, .rt and .rs, plus navigation without BuildContext and 14 page transitions.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#responsive #screen-size #navigation #page-transition #animation

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flexify