Use A Package Like A Pro
Tags: dart, flutter, mobile
Published: 2022-10-25 12:53:20

As a developer, you will make mistakes when learning something new. I used to make this particular mistake we are going to talk about, till I realized I couldn't live like that.

I see beginners, import packages and start using them everywhere in their project. This makes it difficult when there's a change of package. They will have to touch too many places to get things working. Or, when there's a breaking changes, they go through the same hell. 

#Now, let's look at using packages the right way

We are going to look a simple package but you can apply this same logic for all kinds of packages.


* Create a class or function to handle the package however you want it. This class could be either widget class or a normal class. 

## Create A Class
This is my class for package_info_plus package which has a function of getting app version.

```
import 'package:package_info_plus/package_info_plus.dart';

class AppPackageInfo {
  static Future<String> getAppVersion() async {
    final packageInfo = await PackageInfo.fromPlatform();
    return '${packageInfo.version}';
  }
}
```

You can use it in any widget you like and if in any case you would like to change the package, all you need to do is change it at one place.

```
class AppVersionScreen extends StatefulWidget {
  const AppVersionScreen({Key? key}) : super(key: key);

  @override
  State<AppVersionScreen> createState() => _AppVersionScreenState();
}

class _AppVersionScreenState extends State<AppVersionScreen> {
  String _appVersion = '';
  void _getAppVersion() async {
    String appVersion = await AppVersion.getAppVersion();
    if (mounted) setState(() => _appVersion = appVersion);
  }
  
  @override
  void initState() {
    _getAppVersion();
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text('$_appVersion'),
    );
  }
}
```

## Let's look at widget package

Say, you want to do a loading effect for your **ListViews**. You could use a package like **shimmer** to create a nice loading effect. You don't want to be using this package anywhere in your project.
If there's no support for **shimmer** package anymore, you have replace it with a similar package like **skeleton**.

This means you will have to touch places you have **shimmer** package and replace **skeleton**.

Instead, create a **custom stateless widget** and create your **Shimmer** effect there. Make it highly customizable so it could suit your use cases.

```
class ShimmerLoadingEffect extends StatelessWidget {
  const ShimmerLoadingEffect({
    Key? key,
    required this.child,
    this.itemCount = 4,
  }) : super(key: key);

  final Widget child;
  final int itemCount;

  @override
  Widget build(BuildContext context) {
    return Shimmer.fromColors(
      baseColor: Colors.grey.shade300,
      highlightColor: Colors.grey.shade100,
      child: ListView.builder(
        padding: EdgeInsets.zero,
        itemCount: itemCount,
        itemBuilder: (context, index){
          return child;
        },
      ),
    );
  }
}
```


This is a simple stateful widget consuming API

```
class ViewPeopleScreen extends StatefulWidget {
  const ViewPeopleScreen({Key? key}) : super(key: key);

  @override
  State<ViewPeopleScreen> createState() => _ViewPeopleScreenState();
}

class _ViewPeopleScreenState extends State<ViewPeopleScreen> {

List _people = [];
  
  void _getPeople() async{
    try{

        var response = await _peopleService.fetchPeople();
        if(!mounted) return;
        setState(() => _people = response['data'] );

    } on TimeoutException catch (t) {
      log(t.toString());
    } catch(e){
      log(e.toString());
    }
  }

  @override
  void initState() {
    _getPeople();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return  _people.length == 0 ?
      ShimmerLoadingEffect()
         :
       ListView.builder(
          itemCount: _people.length,
          itemBuilder: (context, index){
          final people = _people[index];
            return Text(people['name'].toString());
          },
       );
  }
}
```
##Take Away
Whenever there's a change to the **shimmer** package, it will affect one place and one file only. This also allows you to adhere to the **DRY** principle.

These are just simple examples and this technique applies to a complex use cases as well.

Writing application is hard, and as a beginner, if you don't take care, this little mistakes could frustrate you.